mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
gzip: fix flag location in header (#17140)
This commit is contained in:
parent
7f5f69a78a
commit
5cd01ea494
@ -47,23 +47,44 @@ pub struct DecompressParams {
|
|||||||
verify_checksum bool = true
|
verify_checksum bool = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
|
pub const (
|
||||||
// Example: decompressed := gzip.decompress(b)?
|
reserved_bits = 0b1110_0000
|
||||||
pub fn decompress(data []u8, params DecompressParams) ![]u8 {
|
ftext = 0b0000_0001
|
||||||
if data.len < 18 {
|
fextra = 0b0000_0100
|
||||||
|
fname = 0b0000_1000
|
||||||
|
fcomment = 0b0001_0000
|
||||||
|
fhcrc = 0b0000_0010
|
||||||
|
)
|
||||||
|
|
||||||
|
const min_header_length = 18
|
||||||
|
|
||||||
|
[noinit]
|
||||||
|
pub struct GzipHeader {
|
||||||
|
pub mut:
|
||||||
|
length int = 10
|
||||||
|
extra []u8
|
||||||
|
filename []u8
|
||||||
|
comment []u8
|
||||||
|
modification_time u32
|
||||||
|
operating_system u8
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate validates the header and returns its details if valid
|
||||||
|
pub fn validate(data []u8, params DecompressParams) !GzipHeader {
|
||||||
|
if data.len < gzip.min_header_length {
|
||||||
return error('data is too short, not gzip compressed?')
|
return error('data is too short, not gzip compressed?')
|
||||||
} else if data[0] != 0x1f || data[1] != 0x8b {
|
} else if data[0] != 0x1f || data[1] != 0x8b {
|
||||||
return error('wrong magic numbers, not gzip compressed?')
|
return error('wrong magic numbers, not gzip compressed?')
|
||||||
} else if data[2] != 0x08 {
|
} else if data[2] != 0x08 {
|
||||||
return error('gzip data is not compressed with DEFLATE')
|
return error('gzip data is not compressed with DEFLATE')
|
||||||
}
|
}
|
||||||
mut header_length := 10
|
mut header := GzipHeader{}
|
||||||
|
|
||||||
// parse flags, we ignore most of them, but we still need to parse them
|
// parse flags, we ignore most of them, but we still need to parse them
|
||||||
// correctly, so we dont accidently decompress something that belongs
|
// correctly, so we dont accidently decompress something that belongs
|
||||||
// to the header
|
// to the header
|
||||||
|
|
||||||
if data[4] & 0b1110_0000 > 0 { // reserved bits
|
if data[3] & gzip.reserved_bits > 0 {
|
||||||
// rfc 1952 2.3.1.2 Compliance
|
// rfc 1952 2.3.1.2 Compliance
|
||||||
// A compliant decompressor must give an error indication if any
|
// A compliant decompressor must give an error indication if any
|
||||||
// reserved bit is non-zero, since such a bit could indicate the
|
// reserved bit is non-zero, since such a bit could indicate the
|
||||||
@ -72,40 +93,51 @@ pub fn decompress(data []u8, params DecompressParams) ![]u8 {
|
|||||||
return error('reserved flags are set, unsupported field detected')
|
return error('reserved flags are set, unsupported field detected')
|
||||||
}
|
}
|
||||||
|
|
||||||
// if data[4] & 0b0000_0001 {} // FTEXT
|
if data[3] & gzip.fextra > 0 {
|
||||||
if data[4] & 0b0000_0100 > 0 { // FEXTRA, extra data
|
xlen := data[header.length]
|
||||||
xlen := data[header_length]
|
header.extra = data[header.length + 1..header.length + 1 + xlen]
|
||||||
header_length += xlen + 1
|
header.length += xlen + 1
|
||||||
}
|
}
|
||||||
if data[4] & 0b0000_1000 > 0 { // FNAME, file name
|
if data[3] & gzip.fname > 0 {
|
||||||
// filename is zero-terminated, so skip until we hit a zero byte
|
// filename is zero-terminated, so skip until we hit a zero byte
|
||||||
for header_length < data.len && data[header_length] != 0x00 {
|
for header.length < data.len && data[header.length] != 0x00 {
|
||||||
header_length++
|
header.filename << data[header.length]
|
||||||
|
header.length++
|
||||||
}
|
}
|
||||||
header_length++
|
header.length++
|
||||||
}
|
}
|
||||||
if data[4] & 0b0001_0000 > 0 { // FCOMMENT
|
if data[3] & gzip.fcomment > 0 {
|
||||||
// comment is zero-terminated, so skip until we hit a zero byte
|
// comment is zero-terminated, so skip until we hit a zero byte
|
||||||
for header_length < data.len && data[header_length] != 0x00 {
|
for header.length < data.len && data[header.length] != 0x00 {
|
||||||
header_length++
|
header.comment << data[header.length]
|
||||||
|
header.length++
|
||||||
}
|
}
|
||||||
header_length++
|
header.length++
|
||||||
}
|
}
|
||||||
if data[4] & 0b0000_0010 > 0 { // FHCRC, flag header crc
|
if data[3] & gzip.fhcrc > 0 {
|
||||||
if header_length + 12 > data.len {
|
if header.length + 12 > data.len {
|
||||||
return error('data too short')
|
return error('data too short')
|
||||||
}
|
}
|
||||||
checksum_header := crc32.sum(data[..header_length])
|
checksum_header := crc32.sum(data[..header.length])
|
||||||
checksum_header_expected := (u32(data[header_length]) << 24) | (u32(data[header_length + 1]) << 16) | (u32(data[
|
checksum_header_expected := (u32(data[header.length]) << 24) | (u32(data[header.length + 1]) << 16) | (u32(data[
|
||||||
header_length + 2]) << 8) | data[header_length + 3]
|
header.length + 2]) << 8) | data[header.length + 3]
|
||||||
if params.verify_header_checksum && checksum_header != checksum_header_expected {
|
if params.verify_header_checksum && checksum_header != checksum_header_expected {
|
||||||
return error('header checksum verification failed')
|
return error('header checksum verification failed')
|
||||||
}
|
}
|
||||||
header_length += 4
|
header.length += 4
|
||||||
}
|
}
|
||||||
if header_length + 8 > data.len {
|
if header.length + 8 > data.len {
|
||||||
return error('data too short')
|
return error('data too short')
|
||||||
}
|
}
|
||||||
|
header.operating_system = data[9]
|
||||||
|
return header
|
||||||
|
}
|
||||||
|
|
||||||
|
// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array
|
||||||
|
// Example: decompressed := gzip.decompress(b)?
|
||||||
|
pub fn decompress(data []u8, params DecompressParams) ![]u8 {
|
||||||
|
gzip_header := validate(data, params)!
|
||||||
|
header_length := gzip_header.length
|
||||||
|
|
||||||
decompressed := compress.decompress(data[header_length..data.len - 8], 0)!
|
decompressed := compress.decompress(data[header_length..data.len - 8], 0)!
|
||||||
length_expected := (u32(data[data.len - 4]) << 24) | (u32(data[data.len - 3]) << 16) | (u32(data[data.len - 2]) << 8) | data[data.len - 1]
|
length_expected := (u32(data[data.len - 4]) << 24) | (u32(data[data.len - 3]) << 16) | (u32(data[data.len - 2]) << 8) | data[data.len - 1]
|
||||||
|
@ -35,7 +35,7 @@ fn test_gzip_invalid_compression() {
|
|||||||
fn test_gzip_with_ftext() {
|
fn test_gzip_with_ftext() {
|
||||||
uncompressed := 'Hello world!'
|
uncompressed := 'Hello world!'
|
||||||
mut compressed := compress(uncompressed.bytes())!
|
mut compressed := compress(uncompressed.bytes())!
|
||||||
compressed[4] |= 0b0000_0001 // FTEXT
|
compressed[3] |= ftext
|
||||||
decompressed := decompress(compressed)!
|
decompressed := decompress(compressed)!
|
||||||
assert decompressed == uncompressed.bytes()
|
assert decompressed == uncompressed.bytes()
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ fn test_gzip_with_ftext() {
|
|||||||
fn test_gzip_with_fname() {
|
fn test_gzip_with_fname() {
|
||||||
uncompressed := 'Hello world!'
|
uncompressed := 'Hello world!'
|
||||||
mut compressed := compress(uncompressed.bytes())!
|
mut compressed := compress(uncompressed.bytes())!
|
||||||
compressed[4] |= 0b0000_1000
|
compressed[3] |= fname
|
||||||
compressed.insert(10, `h`)
|
compressed.insert(10, `h`)
|
||||||
compressed.insert(11, `i`)
|
compressed.insert(11, `i`)
|
||||||
compressed.insert(12, 0x00)
|
compressed.insert(12, 0x00)
|
||||||
@ -54,7 +54,7 @@ fn test_gzip_with_fname() {
|
|||||||
fn test_gzip_with_fcomment() {
|
fn test_gzip_with_fcomment() {
|
||||||
uncompressed := 'Hello world!'
|
uncompressed := 'Hello world!'
|
||||||
mut compressed := compress(uncompressed.bytes())!
|
mut compressed := compress(uncompressed.bytes())!
|
||||||
compressed[4] |= 0b0001_0000
|
compressed[3] |= fcomment
|
||||||
compressed.insert(10, `h`)
|
compressed.insert(10, `h`)
|
||||||
compressed.insert(11, `i`)
|
compressed.insert(11, `i`)
|
||||||
compressed.insert(12, 0x00)
|
compressed.insert(12, 0x00)
|
||||||
@ -65,7 +65,7 @@ fn test_gzip_with_fcomment() {
|
|||||||
fn test_gzip_with_fname_fcomment() {
|
fn test_gzip_with_fname_fcomment() {
|
||||||
uncompressed := 'Hello world!'
|
uncompressed := 'Hello world!'
|
||||||
mut compressed := compress(uncompressed.bytes())!
|
mut compressed := compress(uncompressed.bytes())!
|
||||||
compressed[4] |= 0b0001_1000
|
compressed[3] |= (fname | fcomment)
|
||||||
compressed.insert(10, `h`)
|
compressed.insert(10, `h`)
|
||||||
compressed.insert(11, `i`)
|
compressed.insert(11, `i`)
|
||||||
compressed.insert(12, 0x00)
|
compressed.insert(12, 0x00)
|
||||||
@ -79,7 +79,7 @@ fn test_gzip_with_fname_fcomment() {
|
|||||||
fn test_gzip_with_fextra() {
|
fn test_gzip_with_fextra() {
|
||||||
uncompressed := 'Hello world!'
|
uncompressed := 'Hello world!'
|
||||||
mut compressed := compress(uncompressed.bytes())!
|
mut compressed := compress(uncompressed.bytes())!
|
||||||
compressed[4] |= 0b0000_0100
|
compressed[3] |= fextra
|
||||||
compressed.insert(10, 2)
|
compressed.insert(10, 2)
|
||||||
compressed.insert(11, `h`)
|
compressed.insert(11, `h`)
|
||||||
compressed.insert(12, `i`)
|
compressed.insert(12, `i`)
|
||||||
@ -90,7 +90,7 @@ fn test_gzip_with_fextra() {
|
|||||||
fn test_gzip_with_hcrc() {
|
fn test_gzip_with_hcrc() {
|
||||||
uncompressed := 'Hello world!'
|
uncompressed := 'Hello world!'
|
||||||
mut compressed := compress(uncompressed.bytes())!
|
mut compressed := compress(uncompressed.bytes())!
|
||||||
compressed[4] |= 0b0000_0010
|
compressed[3] |= fhcrc
|
||||||
checksum := crc32.sum(compressed[..10])
|
checksum := crc32.sum(compressed[..10])
|
||||||
compressed.insert(10, u8(checksum >> 24))
|
compressed.insert(10, u8(checksum >> 24))
|
||||||
compressed.insert(11, u8(checksum >> 16))
|
compressed.insert(11, u8(checksum >> 16))
|
||||||
@ -103,7 +103,7 @@ fn test_gzip_with_hcrc() {
|
|||||||
fn test_gzip_with_invalid_hcrc() {
|
fn test_gzip_with_invalid_hcrc() {
|
||||||
uncompressed := 'Hello world!'
|
uncompressed := 'Hello world!'
|
||||||
mut compressed := compress(uncompressed.bytes())!
|
mut compressed := compress(uncompressed.bytes())!
|
||||||
compressed[4] |= 0b0000_0010
|
compressed[3] |= fhcrc
|
||||||
checksum := crc32.sum(compressed[..10])
|
checksum := crc32.sum(compressed[..10])
|
||||||
compressed.insert(10, u8(checksum >> 24))
|
compressed.insert(10, u8(checksum >> 24))
|
||||||
compressed.insert(11, u8(checksum >> 16))
|
compressed.insert(11, u8(checksum >> 16))
|
||||||
@ -129,6 +129,6 @@ fn test_gzip_with_invalid_length() {
|
|||||||
fn test_gzip_with_invalid_flags() {
|
fn test_gzip_with_invalid_flags() {
|
||||||
uncompressed := 'Hello world!'
|
uncompressed := 'Hello world!'
|
||||||
mut compressed := compress(uncompressed.bytes())!
|
mut compressed := compress(uncompressed.bytes())!
|
||||||
compressed[4] |= 0b1000_0000
|
compressed[3] |= 0b1000_0000
|
||||||
assert_decompress_error(compressed, 'reserved flags are set, unsupported field detected')!
|
assert_decompress_error(compressed, 'reserved flags are set, unsupported field detected')!
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user