Inflate.java
/*
* Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided with
* the distribution.
*
* 3. The names of the authors may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This program is based on zlib-1.1.3, so all credit should go authors Jean-loup
* Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib.
*/
package com.jcraft.jsch.jzlib;
import java.io.ByteArrayOutputStream;
final class Inflate {
private static final int MAX_WBITS = 15; // 32K LZ77 window
// preset dictionary flag in zlib header
private static final int PRESET_DICT = 0x20;
static final int Z_NO_FLUSH = 0;
static final int Z_PARTIAL_FLUSH = 1;
static final int Z_SYNC_FLUSH = 2;
static final int Z_FULL_FLUSH = 3;
static final int Z_FINISH = 4;
private static final int Z_DEFLATED = 8;
private static final int Z_OK = 0;
private static final int Z_STREAM_END = 1;
private static final int Z_NEED_DICT = 2;
private static final int Z_ERRNO = -1;
private static final int Z_STREAM_ERROR = -2;
private static final int Z_DATA_ERROR = -3;
private static final int Z_MEM_ERROR = -4;
private static final int Z_BUF_ERROR = -5;
private static final int Z_VERSION_ERROR = -6;
private static final int METHOD = 0; // waiting for method byte
private static final int FLAG = 1; // waiting for flag byte
private static final int DICT4 = 2; // four dictionary check bytes to go
private static final int DICT3 = 3; // three dictionary check bytes to go
private static final int DICT2 = 4; // two dictionary check bytes to go
private static final int DICT1 = 5; // one dictionary check byte to go
private static final int DICT0 = 6; // waiting for inflateSetDictionary
private static final int BLOCKS = 7; // decompressing blocks
private static final int CHECK4 = 8; // four check bytes to go
private static final int CHECK3 = 9; // three check bytes to go
private static final int CHECK2 = 10; // two check bytes to go
private static final int CHECK1 = 11; // one check byte to go
private static final int DONE = 12; // finished check, done
private static final int BAD = 13; // got an error--stay here
private static final int HEAD = 14;
private static final int LENGTH = 15;
private static final int TIME = 16;
private static final int OS = 17;
private static final int EXLEN = 18;
private static final int EXTRA = 19;
private static final int NAME = 20;
private static final int COMMENT = 21;
private static final int HCRC = 22;
private static final int FLAGS = 23;
static final int INFLATE_ANY = 0x40000000;
int mode; // current inflate mode
// mode dependent information
int method; // if FLAGS, method byte
// if CHECK, check values to compare
long was = -1; // computed check value
long need; // stream check value
// if BAD, inflateSync's marker bytes count
int marker;
// mode independent information
int wrap; // flag for no wrapper
// 0: no wrapper
// 1: zlib header
// 2: gzip header
// 4: auto detection
int wbits; // log2(window size) (8..15, defaults to 15)
InfBlocks blocks; // current inflate_blocks state
private final ZStream z;
private int flags;
private int need_bytes = -1;
private byte[] crcbuf = new byte[4];
GZIPHeader gheader = null;
int inflateReset() {
if (z == null)
return Z_STREAM_ERROR;
z.total_in = z.total_out = 0;
z.msg = null;
this.mode = HEAD;
this.need_bytes = -1;
this.blocks.reset();
return Z_OK;
}
int inflateEnd() {
if (blocks != null) {
blocks.free();
}
return Z_OK;
}
Inflate(ZStream z) {
this.z = z;
}
int inflateInit(int w) {
z.msg = null;
blocks = null;
// handle undocumented wrap option (no zlib header or check)
wrap = 0;
if (w < 0) {
w = -w;
} else if ((w & INFLATE_ANY) != 0) {
wrap = 4;
w &= ~INFLATE_ANY;
if (w < 48)
w &= 15;
} else if ((w & ~31) != 0) { // for example, DEF_WBITS + 32
wrap = 4; // zlib and gzip wrapped data should be accepted.
w &= 15;
} else {
wrap = (w >> 4) + 1;
if (w < 48)
w &= 15;
}
if (w < 8 || w > 15) {
inflateEnd();
return Z_STREAM_ERROR;
}
if (blocks != null && wbits != w) {
blocks.free();
blocks = null;
}
// set window size
wbits = w;
this.blocks = new InfBlocks(z, 1 << w);
// reset state
inflateReset();
return Z_OK;
}
@SuppressWarnings("fallthrough")
int inflate(int f) {
int hold = 0;
int r;
int b;
if (z == null || z.next_in == null) {
if (f == Z_FINISH && this.mode == HEAD)
return Z_OK;
return Z_STREAM_ERROR;
}
f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK;
r = Z_BUF_ERROR;
while (true) {
switch (this.mode) {
case HEAD:
if (wrap == 0) {
this.mode = BLOCKS;
break;
}
try {
r = readBytes(2, r, f);
} catch (Return e) {
return e.r;
}
if ((wrap == 4 || (wrap & 2) != 0) && this.need == 0x8b1fL) { // gzip header
if (wrap == 4) {
wrap = 2;
}
z.adler = new CRC32();
checksum(2, this.need);
if (gheader == null)
gheader = new GZIPHeader();
this.mode = FLAGS;
break;
}
if ((wrap & 2) != 0) {
this.mode = BAD;
z.msg = "incorrect header check";
break;
}
flags = 0;
this.method = ((int) this.need) & 0xff;
b = ((int) (this.need >> 8)) & 0xff;
if (((wrap & 1) == 0 || // check if zlib header allowed
(((this.method << 8) + b) % 31) != 0) && (this.method & 0xf) != Z_DEFLATED) {
if (wrap == 4) {
z.next_in_index -= 2;
z.avail_in += 2;
z.total_in -= 2;
wrap = 0;
this.mode = BLOCKS;
break;
}
this.mode = BAD;
z.msg = "incorrect header check";
// since zlib 1.2, it is allowted to inflateSync for this case.
/*
* this.marker = 5; // can't try inflateSync
*/
break;
}
if ((this.method & 0xf) != Z_DEFLATED) {
this.mode = BAD;
z.msg = "unknown compression method";
// since zlib 1.2, it is allowted to inflateSync for this case.
/*
* this.marker = 5; // can't try inflateSync
*/
break;
}
if (wrap == 4) {
wrap = 1;
}
if ((this.method >> 4) + 8 > this.wbits) {
this.mode = BAD;
z.msg = "invalid window size";
// since zlib 1.2, it is allowted to inflateSync for this case.
/*
* this.marker = 5; // can't try inflateSync
*/
break;
}
z.adler = new Adler32();
if ((b & PRESET_DICT) == 0) {
this.mode = BLOCKS;
break;
}
this.mode = DICT4;
case DICT4:
if (z.avail_in == 0)
return r;
r = f;
z.avail_in--;
z.total_in++;
this.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & 0xff000000L;
this.mode = DICT3;
case DICT3:
if (z.avail_in == 0)
return r;
r = f;
z.avail_in--;
z.total_in++;
this.need += ((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L;
this.mode = DICT2;
case DICT2:
if (z.avail_in == 0)
return r;
r = f;
z.avail_in--;
z.total_in++;
this.need += ((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L;
this.mode = DICT1;
case DICT1:
if (z.avail_in == 0)
return r;
r = f;
z.avail_in--;
z.total_in++;
this.need += (z.next_in[z.next_in_index++] & 0xffL);
z.adler.reset(this.need);
this.mode = DICT0;
return Z_NEED_DICT;
case DICT0:
this.mode = BAD;
z.msg = "need dictionary";
this.marker = 0; // can try inflateSync
return Z_STREAM_ERROR;
case BLOCKS:
r = this.blocks.proc(r);
if (r == Z_DATA_ERROR) {
this.mode = BAD;
this.marker = 0; // can try inflateSync
break;
}
if (r == Z_OK) {
r = f;
}
if (r != Z_STREAM_END) {
return r;
}
r = f;
this.was = z.adler.getValue();
this.blocks.reset();
if (this.wrap == 0) {
this.mode = DONE;
break;
}
this.mode = CHECK4;
case CHECK4:
if (z.avail_in == 0)
return r;
r = f;
z.avail_in--;
z.total_in++;
this.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & 0xff000000L;
this.mode = CHECK3;
case CHECK3:
if (z.avail_in == 0)
return r;
r = f;
z.avail_in--;
z.total_in++;
this.need += ((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L;
this.mode = CHECK2;
case CHECK2:
if (z.avail_in == 0)
return r;
r = f;
z.avail_in--;
z.total_in++;
this.need += ((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L;
this.mode = CHECK1;
case CHECK1:
if (z.avail_in == 0)
return r;
r = f;
z.avail_in--;
z.total_in++;
this.need += (z.next_in[z.next_in_index++] & 0xffL);
if (flags != 0) { // gzip
this.need = ((this.need & 0xff000000) >> 24 | (this.need & 0x00ff0000) >> 8
| (this.need & 0x0000ff00) << 8 | (this.need & 0x0000ffff) << 24) & 0xffffffffL;
}
if (((int) (this.was)) != ((int) (this.need))) {
z.msg = "incorrect data check";
// chack is delayed
/*
* this.mode = BAD; this.marker = 5; // can't try inflateSync break;
*/
} else if (flags != 0 && gheader != null) {
gheader.crc = this.need;
}
this.mode = LENGTH;
case LENGTH:
if (wrap != 0 && flags != 0) {
try {
r = readBytes(4, r, f);
} catch (Return e) {
return e.r;
}
if (z.msg != null && z.msg.equals("incorrect data check")) {
this.mode = BAD;
this.marker = 5; // can't try inflateSync
break;
}
if (this.need != (z.total_out & 0xffffffffL)) {
z.msg = "incorrect length check";
this.mode = BAD;
break;
}
z.msg = null;
} else {
if (z.msg != null && z.msg.equals("incorrect data check")) {
this.mode = BAD;
this.marker = 5; // can't try inflateSync
break;
}
}
this.mode = DONE;
case DONE:
return Z_STREAM_END;
case BAD:
return Z_DATA_ERROR;
case FLAGS:
try {
r = readBytes(2, r, f);
} catch (Return e) {
return e.r;
}
flags = ((int) this.need) & 0xffff;
if ((flags & 0xff) != Z_DEFLATED) {
z.msg = "unknown compression method";
this.mode = BAD;
break;
}
if ((flags & 0xe000) != 0) {
z.msg = "unknown header flags set";
this.mode = BAD;
break;
}
if ((flags & 0x0200) != 0) {
checksum(2, this.need);
}
this.mode = TIME;
case TIME:
try {
r = readBytes(4, r, f);
} catch (Return e) {
return e.r;
}
if (gheader != null) {
gheader.setModifiedTime(this.need);
}
if ((flags & 0x0200) != 0) {
checksum(4, this.need);
}
this.mode = OS;
case OS:
try {
r = readBytes(2, r, f);
} catch (Return e) {
return e.r;
}
if (gheader != null) {
gheader.xflags = ((int) this.need) & 0xff;
gheader.os = (((int) this.need) >> 8) & 0xff;
}
if ((flags & 0x0200) != 0) {
checksum(2, this.need);
}
this.mode = EXLEN;
case EXLEN:
if ((flags & 0x0400) != 0) {
try {
r = readBytes(2, r, f);
} catch (Return e) {
return e.r;
}
if (gheader != null) {
gheader.extra = new byte[((int) this.need) & 0xffff];
}
if ((flags & 0x0200) != 0) {
checksum(2, this.need);
}
} else if (gheader != null) {
gheader.extra = null;
}
this.mode = EXTRA;
case EXTRA:
if ((flags & 0x0400) != 0) {
try {
r = readBytes(r, f);
if (gheader != null) {
byte[] foo = tmp_string.toByteArray();
tmp_string = null;
if (foo.length == gheader.extra.length) {
System.arraycopy(foo, 0, gheader.extra, 0, foo.length);
} else {
z.msg = "bad extra field length";
this.mode = BAD;
break;
}
}
} catch (Return e) {
return e.r;
}
} else if (gheader != null) {
gheader.extra = null;
}
this.mode = NAME;
case NAME:
if ((flags & 0x0800) != 0) {
try {
r = readString(r, f);
if (gheader != null) {
gheader.name = tmp_string.toByteArray();
}
tmp_string = null;
} catch (Return e) {
return e.r;
}
} else if (gheader != null) {
gheader.name = null;
}
this.mode = COMMENT;
case COMMENT:
if ((flags & 0x1000) != 0) {
try {
r = readString(r, f);
if (gheader != null) {
gheader.comment = tmp_string.toByteArray();
}
tmp_string = null;
} catch (Return e) {
return e.r;
}
} else if (gheader != null) {
gheader.comment = null;
}
this.mode = HCRC;
case HCRC:
if ((flags & 0x0200) != 0) {
try {
r = readBytes(2, r, f);
} catch (Return e) {
return e.r;
}
if (gheader != null) {
gheader.hcrc = (int) (this.need & 0xffff);
}
if (this.need != (z.adler.getValue() & 0xffffL)) {
this.mode = BAD;
z.msg = "header crc mismatch";
this.marker = 5; // can't try inflateSync
break;
}
}
z.adler = new CRC32();
this.mode = BLOCKS;
break;
default:
return Z_STREAM_ERROR;
}
}
}
int inflateSetDictionary(byte[] dictionary, int dictLength) {
if (z == null || (this.mode != DICT0 && this.wrap != 0)) {
return Z_STREAM_ERROR;
}
int index = 0;
int length = dictLength;
if (this.mode == DICT0) {
long adler_need = z.adler.getValue();
z.adler.reset();
z.adler.update(dictionary, 0, dictLength);
if (z.adler.getValue() != adler_need) {
return Z_DATA_ERROR;
}
}
z.adler.reset();
if (length >= (1 << this.wbits)) {
length = (1 << this.wbits) - 1;
index = dictLength - length;
}
this.blocks.set_dictionary(dictionary, index, length);
this.mode = BLOCKS;
return Z_OK;
}
private static byte[] mark = {(byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff};
int inflateSync() {
int n; // number of bytes to look at
int p; // pointer to bytes
int m; // number of marker bytes found in a row
long r, w; // temporaries to save total_in and total_out
// set up
if (z == null)
return Z_STREAM_ERROR;
if (this.mode != BAD) {
this.mode = BAD;
this.marker = 0;
}
if ((n = z.avail_in) == 0)
return Z_BUF_ERROR;
p = z.next_in_index;
m = this.marker;
// search
while (n != 0 && m < 4) {
if (z.next_in[p] == mark[m]) {
m++;
} else if (z.next_in[p] != 0) {
m = 0;
} else {
m = 4 - m;
}
p++;
n--;
}
// restore
z.total_in += p - z.next_in_index;
z.next_in_index = p;
z.avail_in = n;
this.marker = m;
// return no joy or set up to restart on a new block
if (m != 4) {
return Z_DATA_ERROR;
}
r = z.total_in;
w = z.total_out;
inflateReset();
z.total_in = r;
z.total_out = w;
this.mode = BLOCKS;
return Z_OK;
}
// Returns true if inflate is currently at the end of a block generated
// by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP
// implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH
// but removes the length bytes of the resulting empty stored block. When
// decompressing, PPP checks that at the end of input packet, inflate is
// waiting for these length bytes.
int inflateSyncPoint() {
if (z == null || this.blocks == null)
return Z_STREAM_ERROR;
return this.blocks.sync_point();
}
private int readBytes(int n, int r, int f) throws Return {
if (need_bytes == -1) {
need_bytes = n;
this.need = 0;
}
while (need_bytes > 0) {
if (z.avail_in == 0) {
throw new Return(r);
} ;
r = f;
z.avail_in--;
z.total_in++;
this.need = this.need | ((z.next_in[z.next_in_index++] & 0xff) << ((n - need_bytes) * 8));
need_bytes--;
}
if (n == 2) {
this.need &= 0xffffL;
} else if (n == 4) {
this.need &= 0xffffffffL;
}
need_bytes = -1;
return r;
}
static class Return extends Exception {
private static final long serialVersionUID = -1L;
int r;
Return(int r) {
this.r = r;
}
}
private ByteArrayOutputStream tmp_string = null;
private int readString(int r, int f) throws Return {
if (tmp_string == null) {
tmp_string = new ByteArrayOutputStream();
}
int b = 0;
do {
if (z.avail_in == 0) {
throw new Return(r);
} ;
r = f;
z.avail_in--;
z.total_in++;
b = z.next_in[z.next_in_index];
if (b != 0)
tmp_string.write(z.next_in, z.next_in_index, 1);
z.adler.update(z.next_in, z.next_in_index, 1);
z.next_in_index++;
} while (b != 0);
return r;
}
private int readBytes(int r, int f) throws Return {
if (tmp_string == null) {
tmp_string = new ByteArrayOutputStream();
}
int b = 0;
while (this.need > 0) {
if (z.avail_in == 0) {
throw new Return(r);
} ;
r = f;
z.avail_in--;
z.total_in++;
b = z.next_in[z.next_in_index];
tmp_string.write(z.next_in, z.next_in_index, 1);
z.adler.update(z.next_in, z.next_in_index, 1);
z.next_in_index++;
this.need--;
}
return r;
}
private void checksum(int n, long v) {
for (int i = 0; i < n; i++) {
crcbuf[i] = (byte) (v & 0xff);
v >>= 8;
}
z.adler.update(crcbuf, 0, n);
}
GZIPHeader getGZIPHeader() {
return gheader;
}
boolean inParsingHeader() {
switch (mode) {
case HEAD:
case DICT4:
case DICT3:
case DICT2:
case DICT1:
case FLAGS:
case TIME:
case OS:
case EXLEN:
case EXTRA:
case NAME:
case COMMENT:
case HCRC:
return true;
default:
return false;
}
}
}