diff --git a/docs/changes.txt b/docs/changes.txt index ac15670..f15af51 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -162,3 +162,7 @@ v0.8.1 * init.d: 80-dns-intercept * winws2: --wf-filter-loopback * blockcheck2: NOTEST_MISC_HTTP[S], NOTEST_SYNDATA_HTTP[S] + +0.8.3 + +* nfqws2, zapret-lib: gzip compression and decompression diff --git a/lua/zapret-lib.lua b/lua/zapret-lib.lua index d223b88..debcdae 100644 --- a/lua/zapret-lib.lua +++ b/lua/zapret-lib.lua @@ -1426,6 +1426,83 @@ function tls_client_hello_mod(tls, options) return tls end +-- checks if filename is gzip compressed +function is_gzip_file(filename) + local f, err = io.open(filename, "r") + if not f then + error("gunzip_file: "..err) + end + local hdr = f:read(2) + f:close() + return hdr and hdr=="\x1F\x8B" +end +-- ungzips file to raw string +function gunzip_file(filename, read_block_size) + local f, err = io.open(filename, "r") + if not f then + error("gunzip_file: "..err) + end + if not read_block_size then read_block_size=16384 end + + local decompressed="" + gz = gunzip_init() + if not gz then + error("gunzip_file: stream init error") + end + repeat + local compressed, err = f:read(read_block_size) + if not compressed then + f:close() + gunzip_end(gz) + if err then + error("gunzip_file: file read error : "..err) + else + error("gunzip_file: premature EOF") + end + end + local decomp, eof = gunzip_inflate(gz, compressed) + if not decomp then + f:close() + gunzip_end(gz) + return nil + end + decompressed = decompressed .. decomp + until eof + f:close() + gunzip_end(gz) + return decompressed +end +-- zips file to raw string +-- level : 1..9 (default 9) +-- memlevel : 1..8 (default 8) +function gzip_file(filename, data, level, memlevel, compress_block_size) + local f, err = io.open(filename, "w") + if not f then + error("gzip_file: "..err) + end + if not write_block_size then compress_block_size=16384 end + + gz = gzip_init(nil, level, memlevel) + if not gz then + error("gunzip_file: stream init error") + end + local off=1, block_size + repeat + block_size = #data-off+1 + if block_size>compress_block_size then block_size=compress_block_size end + local comp, eof = gzip_deflate(gz, string.sub(data,off,off+block_size-1)) + if not comp then + f:close() + gzip_end(gz) + return nil + end + f:write(comp) + off = off + block_size + until eof + f:close() + gzip_end(gz) +end + -- DISSECTORS function http_dissect_header(header) diff --git a/nfq2/lua.c b/nfq2/lua.c index b75991c..2026920 100644 --- a/nfq2/lua.c +++ b/nfq2/lua.c @@ -2591,7 +2591,210 @@ static int luacall_tls_mod(lua_State *L) LUA_STACK_GUARD_RETURN(L,2) } +struct userdata_zs +{ + bool valid, inflate; + z_stream zs; +}; +static struct userdata_zs *lua_uzs(int idx, bool bInflate) +{ + struct userdata_zs *uzs = (struct userdata_zs *)luaL_checkudata(params.L, idx, "userdata_zstream"); + if (!uzs->valid) luaL_error(params.L, "gzip stream is not valid"); + if (bInflate!=uzs->inflate) luaL_error(params.L, "gzip stream role mismatch"); + return uzs; +} +static int luacall_gunzip_init(lua_State *L) +{ + // gunzip_init(windowBits) return zstream + lua_check_argc_range(L,"gunzip_init",0,1); + LUA_STACK_GUARD_ENTER(L) + + int argc=lua_gettop(L); + int windowBits = (argc>=1 && !lua_isnil(L,1)) ? luaL_checkinteger(L, 1) : 47; + + struct userdata_zs *uzs = (struct userdata_zs *)lua_newuserdata(L, sizeof(struct userdata_zs)); + memset(&uzs->zs, 0, sizeof(uzs->zs)); + int r = inflateInit2(&uzs->zs, windowBits); + if (r == Z_OK) + { + uzs->inflate = true; + uzs->valid = true; + luaL_newmetatable(L, "userdata_zstream"); + lua_setmetatable(L, -2); + } + else + { + lua_pop(L,1); + lua_pushnil(L); + } + + LUA_STACK_GUARD_RETURN(L,1) +} +static int luacall_gunzip_end(lua_State *L) +{ + lua_check_argc(L,"gunzip_end",1); + + LUA_STACK_GUARD_ENTER(L) + + struct userdata_zs *uzs = lua_uzs(1, true); + inflateEnd(&uzs->zs); + uzs->valid = false; + + LUA_STACK_GUARD_RETURN(L,0) +} +#define BUFMIN 128 +static int luacall_gunzip_inflate(lua_State *L) +{ + // gunzip_inflate(zstream, compressed_data, expected_uncompressed_chunk_size) return decompressed_data + lua_check_argc_range(L,"gunzip_inflate",2,3); + + LUA_STACK_GUARD_ENTER(L) + + int argc=lua_gettop(L); + size_t l; + int r; + size_t bufsize=0, size=0; + uint8_t *buf=NULL, *newbuf; + struct userdata_zs *uzs = lua_uzs(1, true); + uzs->zs.next_in = (z_const Bytef*)luaL_checklstring(L,2,&l); + uzs->zs.avail_in = (uInt)l; + size_t bufchunk = argc>=3 ? luaL_checkinteger(L,3) : l*4; + + do + { + if ((bufsize - size) < BUFMIN) + { + bufsize += bufchunk; + newbuf = buf ? realloc(buf, bufsize) : malloc(bufsize); + if (!newbuf) + { + r = Z_MEM_ERROR; + goto zerr; + } + buf = newbuf; + } + uzs->zs.avail_out = bufsize - size; + uzs->zs.next_out = buf + size; + r = inflate(&uzs->zs, Z_NO_FLUSH); + if (r != Z_OK && r != Z_STREAM_END) goto zerr; + size = bufsize - uzs->zs.avail_out; + } while (r == Z_OK && uzs->zs.avail_in); + + lua_pushlstring(L, buf, size); + lua_pushboolean(L, r==Z_STREAM_END); +end: + free(buf); + LUA_STACK_GUARD_RETURN(L,2) +zerr: + lua_pushnil(L); + lua_pushnil(L); + goto end; +} + +static void *z_alloc(voidpf opaque, uInt items, uInt size) +{ + return malloc((size_t)items*size); +} +static void z_free(voidpf opaque, voidpf address) +{ + return free(address); +} +static int luacall_gzip_init(lua_State *L) +{ + // gzip_init(windowBits, level, memlevel) return zstream + lua_check_argc_range(L,"gunzip_init",0,3); + + LUA_STACK_GUARD_ENTER(L) + + int argc=lua_gettop(L); + int windowBits = (argc>=1 && !lua_isnil(L,1)) ? luaL_checkinteger(L, 1) : 31; + int level = (argc>=2 && !lua_isnil(L,2)) ? luaL_checkinteger(L, 2) : 9; + int memlevel = (argc>=3 && !lua_isnil(L,3)) ? luaL_checkinteger(L, 3) : 8; + + struct userdata_zs *uzs = (struct userdata_zs *)lua_newuserdata(L, sizeof(struct userdata_zs)); + memset(&uzs->zs, 0, sizeof(uzs->zs)); + uzs->zs.zalloc = z_alloc; + uzs->zs.zfree = z_free; + int r = deflateInit2(&uzs->zs, level, Z_DEFLATED, windowBits, memlevel, Z_DEFAULT_STRATEGY); + if (r == Z_OK) + { + uzs->inflate = false; + uzs->valid = true; + luaL_newmetatable(L, "userdata_zstream"); + lua_setmetatable(L, -2); + } + else + { + lua_pop(L,1); + lua_pushnil(L); + } + + LUA_STACK_GUARD_RETURN(L,1) +} +static int luacall_gzip_end(lua_State *L) +{ + lua_check_argc(L,"gzip_end",1); + + LUA_STACK_GUARD_ENTER(L) + + struct userdata_zs *uzs = lua_uzs(1, false); + deflateEnd(&uzs->zs); + uzs->valid = false; + + LUA_STACK_GUARD_RETURN(L,0) +} + +static int luacall_gzip_deflate(lua_State *L) +{ + // gzip_deflate(zstream, decompressed_data, expected_compressed_chunk_size) return compressed_data + lua_check_argc_range(L,"gzip_deflate",1,3); + + LUA_STACK_GUARD_ENTER(L) + + int argc=lua_gettop(L); + size_t l=0; + int r; + size_t bufsize=0, size=0; + uint8_t *buf=NULL, *newbuf; + struct userdata_zs *uzs = lua_uzs(1, false); + if (argc>=2 && !lua_isnil(L,2)) + { + uzs->zs.next_in = (z_const Bytef*)luaL_checklstring(L,2,&l); + uzs->zs.avail_in = (uInt)l; + } + size_t bufchunk = argc>=3 ? luaL_checkinteger(L,3) : 1+l/4; + + do + { + if ((bufsize - size) < BUFMIN) + { + bufsize += bufchunk; + newbuf = buf ? realloc(buf, bufsize) : malloc(bufsize); + if (!newbuf) + { + r = Z_MEM_ERROR; + goto zerr; + } + buf = newbuf; + } + uzs->zs.avail_out = bufsize - size; + uzs->zs.next_out = buf + size; + r = deflate(&uzs->zs, l ? Z_NO_FLUSH : Z_FINISH); + if (r != Z_OK && r != Z_STREAM_END) goto zerr; + size = bufsize - uzs->zs.avail_out; + } while (r == Z_OK && (uzs->zs.avail_in || !uzs->zs.avail_out)); + + lua_pushlstring(L, buf, size); + lua_pushboolean(L, r==Z_STREAM_END); +end: + free(buf); + LUA_STACK_GUARD_RETURN(L,2) +zerr: + lua_pushnil(L); + lua_pushnil(L); + goto end; +} // ---------------------------------------- @@ -3125,7 +3328,16 @@ static void lua_init_functions(void) {"resolve_range",luacall_resolve_range}, // tls - {"tls_mod",luacall_tls_mod} + {"tls_mod",luacall_tls_mod}, + + // gzip decompress + {"gunzip_init",luacall_gunzip_init}, + {"gunzip_end",luacall_gunzip_end}, + {"gunzip_inflate",luacall_gunzip_inflate}, + // gzip compress + {"gzip_init",luacall_gzip_init}, + {"gzip_end",luacall_gzip_end}, + {"gzip_deflate",luacall_gzip_deflate} }; for(int i=0;i<(sizeof(lfunc)/sizeof(*lfunc));i++) lua_register(params.L,lfunc[i].name,lfunc[i].f);