From b57d23f005ee8a22792ecfb0d6e506a139f8c757 Mon Sep 17 00:00:00 2001 From: dmiller Date: Wed, 13 Sep 2017 01:54:19 +0000 Subject: [PATCH] Add zlib binding for NSE. Fixes #532 --- CHANGELOG | 3 + Makefile.in | 5 + mswin32/nmap.vcxproj | 2 + nse_main.cc | 4 + nse_zlib.cc | 988 +++++++++++++++++++++++++++++++++++++++++++ nse_zlib.h | 9 + nselib/unittest.lua | 1 + nselib/zlib.luadoc | 111 +++++ 8 files changed, 1123 insertions(+) create mode 100644 nse_zlib.cc create mode 100644 nse_zlib.h create mode 100644 nselib/zlib.luadoc diff --git a/CHANGELOG b/CHANGELOG index 0bdc450221..7d961527b7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ #s wa Nmap Changelog ($Id$); -*-text-*- +o [NSE][GH#532] Added zlib library for NSE. This was a leftover project from + GSOC 2014, and will be very useful. [Claudiu Perta, Daniel Miller] + o [NSE][GH#1004] Fixed handling of brute.retries variable. It was being treated as the number of tries, not retries, and a value of 0 would result in infinite retries. Instead, it is now the number of retries, defaulting to 2 diff --git a/Makefile.in b/Makefile.in index d7dab46c7f..82d330dff2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -105,6 +105,11 @@ NSE_SRC+=nse_libssh2.cc NSE_HDRS+=nse_libssh2.h NSE_OBJS+=nse_libssh2.o endif +ifneq (@LIBZLIB_LIBS@,) +NSE_SRC+=nse_zlib.cc +NSE_HDRS+=nse_zlib.h +NSE_OBJS+=nse_zlib.o +endif endif export SRCS = charpool.cc FingerPrintResults.cc FPEngine.cc FPModel.cc idle_scan.cc MACLookup.cc main.cc nmap.cc nmap_dns.cc nmap_error.cc nmap_ftp.cc NmapOps.cc NmapOutputTable.cc nmap_tty.cc osscan2.cc osscan.cc output.cc payload.cc portlist.cc portreasons.cc protocols.cc scan_engine.cc scan_engine_connect.cc scan_engine_raw.cc scan_lists.cc service_scan.cc services.cc Target.cc NewTargets.cc TargetGroup.cc targets.cc tcpip.cc timing.cc traceroute.cc utils.cc xml.cc $(NSE_SRC) diff --git a/mswin32/nmap.vcxproj b/mswin32/nmap.vcxproj index 33957f7e79..50b93ce3a7 100644 --- a/mswin32/nmap.vcxproj +++ b/mswin32/nmap.vcxproj @@ -166,6 +166,7 @@ xcopy /y /d "$(ProjectDir)..\libssh2\win32\$(Configuration)_dll\*.dll" "$(Proje + @@ -221,6 +222,7 @@ xcopy /y /d "$(ProjectDir)..\libssh2\win32\$(Configuration)_dll\*.dll" "$(Proje + diff --git a/nse_main.cc b/nse_main.cc index 64a4d50f34..a3b5f0118b 100644 --- a/nse_main.cc +++ b/nse_main.cc @@ -19,6 +19,7 @@ #include "nse_debug.h" #include "nse_lpeg.h" #include "nse_libssh2.h" +#include "nse_zlib.h" #include @@ -551,6 +552,9 @@ static void set_nmap_libraries (lua_State *L) #endif #ifdef HAVE_OPENSSL {OPENSSLLIBNAME, luaopen_openssl}, +#endif +#ifdef HAVE_LIBZ + {NSE_ZLIBNAME, luaopen_zlib}, #endif {NULL, NULL} }; diff --git a/nse_zlib.cc b/nse_zlib.cc new file mode 100644 index 0000000000..85f817035c --- /dev/null +++ b/nse_zlib.cc @@ -0,0 +1,988 @@ +/************************************************************************ +* Author : Tiago Dionizio * +* Library : lzlib - Lua 5 interface to access zlib library functions * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the * +* "Software"), to deal in the Software without restriction, including * +* without limitation the rights to use, copy, modify, merge, publish, * +* distribute, sublicense, and/or sell copies of the Software, and to * +* permit persons to whom the Software is furnished to do so, subject to * +* the following conditions: * +* * +* The above copyright notice and this permission notice shall be * +* included in all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.* +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * +************************************************************************/ + +#include +#include + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +} + +#include + +/* +** ========================================================================= +** compile time options wich determine available functionality +** ========================================================================= +*/ + +/* TODO + +- also call flush on table/userdata when flush function is detected +- remove io_cb check inflate_block if condition +- only set eos when ZSTREAM_END is reached +- check for stream errors to close stream when really needed + +*/ + + +/* +** ========================================================================= +** zlib stream metamethods +** ========================================================================= +*/ +#define ZSTREAMMETA "zlib:zstream" + +#define LZ_ANY -1 +#define LZ_NONE 0 +#define LZ_DEFLATE 1 +#define LZ_INFLATE 2 + +#if 0 + #define LZ_BUFFER_SIZE LUAL_BUFFERSIZE +#else + #define LZ_BUFFER_SIZE 8192 +#endif + +typedef struct { + /* zlib structures */ + z_stream zstream; + /* stream state. LZ_DEFLATE | LZ_INFLATE */ + int state; + int error; + int peek; + int eos; + /* user callback source for reading/writing */ + int io_cb; + /* input buffer */ + int i_buffer_ref; + size_t i_buffer_pos; + size_t i_buffer_len; + const char *i_buffer; + /* output buffer */ + size_t o_buffer_len; + size_t o_buffer_max; + char o_buffer[LZ_BUFFER_SIZE]; + /* dictionary */ + const Bytef *dictionary; + size_t dictionary_len; +} lz_stream; + + +/* forward declarations */ +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush); + + +static lz_stream *lzstream_new(lua_State *L, int src) { + lz_stream *s = (lz_stream*)lua_newuserdata(L, sizeof(lz_stream)); + + luaL_getmetatable(L, ZSTREAMMETA); + lua_setmetatable(L, -2); /* set metatable */ + + s->state = LZ_NONE; + s->error = Z_OK; + s->eos = 0; + s->io_cb = LUA_REFNIL; + + s->i_buffer = NULL; + s->i_buffer_ref = LUA_REFNIL; + s->i_buffer_pos = 0; + s->i_buffer_len = 0; + + s->peek = 0; + s->o_buffer_len = 0; + s->o_buffer_max = sizeof(s->o_buffer) / sizeof(s->o_buffer[0]); + + s->zstream.zalloc = Z_NULL; + s->zstream.zfree = Z_NULL; + + /* prepare source */ + if (lua_isstring(L, src)) { + lua_pushvalue(L, src); + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + s->i_buffer = lua_tolstring(L, src, &s->i_buffer_len); + } else { + /* table | function | userdata */ + lua_pushvalue(L, src); + s->io_cb = luaL_ref(L, LUA_REGISTRYINDEX); + } + return s; +} + +static void lzstream_cleanup(lua_State *L, lz_stream *s) { + if (s && s->state != LZ_NONE) { + if (s->state == LZ_INFLATE) { + inflateEnd(&s->zstream); + } + if (s->state == LZ_DEFLATE) { + deflateEnd(&s->zstream); + } + + luaL_unref(L, LUA_REGISTRYINDEX, s->io_cb); + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->state = LZ_NONE; + } +} + +/* ====================================================================== */ + +static lz_stream *lzstream_get(lua_State *L, int index) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, index, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, index, "bad zlib stream"); + return s; +} + +static lz_stream *lzstream_check(lua_State *L, int index, int state) { + lz_stream *s = lzstream_get(L, index); + if ((state != LZ_ANY && s->state != state) || s->state == LZ_NONE) { + luaL_argerror(L, index, "attempt to use invalid zlib stream"); + } + return s; +} + +/* ====================================================================== */ + +static int lzstream_tostring(lua_State *L) { + lz_stream *s = (lz_stream*)luaL_checkudata(L, 1, ZSTREAMMETA); + if (s == NULL) luaL_argerror(L, 1, "bad zlib stream"); + + if (s->state == LZ_NONE) { + lua_pushstring(L, "zlib stream (closed)"); + } else if (s->state == LZ_DEFLATE) { + lua_pushfstring(L, "zlib deflate stream (%p)", (void*)s); + } else if (s->state == LZ_INFLATE) { + lua_pushfstring(L, "zlib inflate stream (%p)", (void*)s); + } else { + lua_pushfstring(L, "%p", (void*)s); + } + + return 1; +} + +/* ====================================================================== */ + +static int lzstream_gc(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + lzstream_cleanup(L, s); + return 0; +} + +/* ====================================================================== */ + +static int lzstream_close(lua_State *L) { + lz_stream *s = lzstream_get(L, 1); + + if (s->state == LZ_DEFLATE) { + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, Z_FINISH); + } + + lzstream_cleanup(L, s); + lua_pushboolean(L, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_adler(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_ANY); + lua_pushnumber(L, s->zstream.adler); + return 1; +} + +/* ====================================================================== */ + +/* + zlib.deflate( + sink: function | { write: function [, close: function, flush: function] }, + compression level, [Z_DEFAILT_COMPRESSION] + method, [Z_DEFLATED] + windowBits, [15] + memLevel, [8] + strategy, [Z_DEFAULT_STRATEGY] + dictionary: [""] + ) +*/ +static int lzlib_deflate(lua_State *L) { + int level, method, windowBits, memLevel, strategy; + lz_stream *s; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :write function? */ + lua_getfield(L, 1, "write"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "output parameter does not provide :write function"); + } + lua_pop(L, 1); + } + else if (!lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "output parameter must be a function, table or userdata value"); + } + + level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + windowBits = (int) luaL_optinteger(L, 4, 15); + memLevel = (int) luaL_optinteger(L, 5, 8); + strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); + dictionary = luaL_optlstring(L, 7, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (deflateInit2(&s->zstream, level, method, windowBits, memLevel, strategy) != Z_OK) { + lua_pushliteral(L, "call to deflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + if (deflateSetDictionary(&s->zstream, (const Bytef *) dictionary, dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to deflateSetDictionnary failed"); + lua_error(L); + } + } + + s->state = LZ_DEFLATE; + return 1; +} + +/* + zlib.inflate( + source: string | function | { read: function, close: function }, + windowBits: number, [15] + dictionary: [""] + ) +*/ +static int lzlib_inflate(lua_State *L) +{ + int windowBits; + lz_stream *s; + int have_peek = 0; + const char *dictionary; + size_t dictionary_len; + + if (lua_istable(L, 1) || lua_isuserdata(L, 1)) { + /* is there a :read function? */ + lua_getfield(L, 1, "read"); + if (!lua_isfunction(L, -1)) { + luaL_argerror(L, 1, "input parameter does not provide :read function"); + } + lua_pop(L, 1); + /* check for peek function */ + lua_getfield(L, 1, "peek"); + have_peek = lua_isfunction(L, -1); + lua_pop(L, 1); + } + else if (!lua_isstring(L, 1) && !lua_isfunction(L, 1)) { + luaL_argerror(L, 1, "input parameter must be a string, function, table or userdata value"); + } + + windowBits = (int) luaL_optinteger(L, 2, 15); + dictionary = luaL_optlstring(L, 3, NULL, &dictionary_len); + + s = lzstream_new(L, 1); + + if (windowBits > 0 && windowBits < 16) { + windowBits |= 32; + } + + if (inflateInit2(&s->zstream, windowBits) != Z_OK) { + lua_pushliteral(L, "call to inflateInit2 failed"); + lua_error(L); + } + + if (dictionary) { + s->dictionary = (const Bytef *) dictionary; + s->dictionary_len = dictionary_len; + } + + s->peek = have_peek; + s->state = LZ_INFLATE; + return 1; +} + +/* ====================================================================== */ + +static int lz_pushresult (lua_State *L, lz_stream *s) { + if (s->error == Z_OK) { + lua_pushboolean(L, 1); + return 1; + } else { + lua_pushnil(L); + lua_pushstring(L, zError(s->error)); + lua_pushinteger(L, s->error); + return 3; + } +} + +/* + Get block to process: + - top of stack gets +*/ +static const char* lzstream_fetch_block(lua_State *L, lz_stream *s, int hint) { + if (s->i_buffer_pos >= s->i_buffer_len) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isnil(L, -1)) { + if (lua_isfunction(L, -1)) { + lua_pushinteger(L, hint); + lua_call(L, 1, 1); + } else { + lua_getfield(L, -1, (s->peek ? "peek" : "read")); + lua_insert(L, -2); + lua_pushinteger(L, hint); + lua_call(L, 2, 1); + } + + if (lua_isstring(L, -1)) { + s->i_buffer_pos = 0; + s->i_buffer = lua_tolstring(L, -1, &s->i_buffer_len); + if (s->i_buffer_len > 0) { + s->i_buffer_ref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_pop(L, 1); + } + } else if (lua_isnil(L, -1)) { + lua_pop(L, 1); + } else { + lua_pushliteral(L, "deflate callback must return string or nil"); + lua_error(L); + } + } else { + lua_pop(L, 1); + } + } + + return s->i_buffer; +} + +static int lzstream_inflate_block(lua_State *L, lz_stream *s) { + if (lzstream_fetch_block(L, s, LZ_BUFFER_SIZE) || !s->eos) { + int r; + + if (s->i_buffer_len == s->i_buffer_pos) { + s->zstream.next_in = NULL; + s->zstream.avail_in = 0; + } else { + s->zstream.next_in = (unsigned char*)(s->i_buffer + s->i_buffer_pos); + s->zstream.avail_in = s->i_buffer_len - s->i_buffer_pos; + } + + s->zstream.next_out = (unsigned char*)s->o_buffer + s->o_buffer_len; + s->zstream.avail_out = s->o_buffer_max - s->o_buffer_len; + + /* munch some more */ + r = inflate(&s->zstream, Z_SYNC_FLUSH); + + if (r == Z_NEED_DICT) { + if (s->dictionary == NULL) { + lua_pushliteral(L, "no inflate dictionary provided"); + lua_error(L); + } + + if (inflateSetDictionary(&s->zstream, s->dictionary, s->dictionary_len) != Z_OK) { + lua_pushliteral(L, "call to inflateSetDictionnary failed"); + lua_error(L); + } + + r = inflate(&s->zstream, Z_SYNC_FLUSH); + } + + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + s->error = r; + #if 1 + lua_pushfstring(L, "failed to decompress [%d]", r); + lua_error(L); + #endif + } + + if (r == Z_STREAM_END) { + luaL_unref(L, LUA_REGISTRYINDEX, s->i_buffer_ref); + s->i_buffer_ref = LUA_NOREF; + s->i_buffer = NULL; + + s->eos = 1; + } + + /* number of processed bytes */ + if (s->peek) { + size_t processed = s->i_buffer_len - s->i_buffer_pos - s->zstream.avail_in; + + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + lua_getfield(L, -1, "read"); + lua_insert(L, -2); + lua_pushinteger(L, processed); + lua_call(L, 2, 0); + } + + s->i_buffer_pos = s->i_buffer_len - s->zstream.avail_in; + s->o_buffer_len = s->o_buffer_max - s->zstream.avail_out; + } + + return s->o_buffer_len; +} + +/* +** Remove n bytes from the output buffer. +*/ +static void lzstream_remove(lz_stream *s, size_t n) { + memmove(s->o_buffer, s->o_buffer + n, s->o_buffer_len - n); + s->o_buffer_len -= n; +} + +/* +** Copy at most n bytes to buffer b and remove them from the +** output stream buffer. +*/ +static int lzstream_flush_buffer(lua_State *L, lz_stream *s, size_t n, luaL_Buffer *b) { + /* check output */ + if (n > s->o_buffer_len) { + n = s->o_buffer_len; + } + + if (n > 0) { + lua_pushlstring(L, s->o_buffer, n); + luaL_addvalue(b); + + lzstream_remove(s, n); + } + + return n; +} + +/* + z:read( + {number | '*l' | '*a'}* + ) +*/ +static int lz_test_eof(lua_State *L, lz_stream *s) { + lua_pushlstring(L, NULL, 0); + if (s->o_buffer_len > 0) { + return 1; + } else if (s->eos) { + return 0; + } else { + return lzstream_inflate_block(L, s); + } +} + +static int lz_read_line(lua_State *L, lz_stream *s) { + luaL_Buffer b; + size_t l = 0, n; + + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + char *p = s->o_buffer; + size_t len = s->o_buffer_len; + + /* find newline in output buffer */ + for (n = 0; n < len; ++n, ++p) { + if (*p == '\n' || *p == '\r') { + int eat_nl = *p == '\r'; + luaL_addlstring(&b, s->o_buffer, n); + lzstream_remove(s, n+1); + l += n; + + if (eat_nl && lzstream_inflate_block(L, s)) { + if (s->o_buffer_len > 0 && *s->o_buffer == '\n') { + lzstream_remove(s, 1); + } + } + + luaL_pushresult(&b); + return 1; + } + } + + if (len > 0) { + luaL_addlstring(&b, s->o_buffer, len); + lzstream_remove(s, len); + l += len; + } + } while (lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + return l > 0 || !s->eos || s->o_buffer_len > 0; +} + + +static int lz_read_chars(lua_State *L, lz_stream *s, size_t n) { + size_t len; + luaL_Buffer b; + luaL_buffinit(L, &b); + + if (s->o_buffer_len > 0 || !s->eos) do { + size_t rlen = lzstream_flush_buffer(L, s, n, &b); + n -= rlen; + } while (n > 0 && lzstream_inflate_block(L, s)); + + luaL_pushresult(&b); + lua_tolstring(L, -1, &len); + return n == 0 || len > 0; +} + +static int lzstream_decompress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_INFLATE); + int nargs = lua_gettop(L) - 1; + int success; + int n; + if (nargs == 0) { /* no arguments? */ + success = lz_read_line(L, s); + n = 3; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = 2; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? lz_test_eof(L, s) : lz_read_chars(L, s, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'l': /* line */ + success = lz_read_line(L, s); + break; + case 'a': /* file */ + lz_read_chars(L, s, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - 2; +} + + +static int lzstream_readline(lua_State *L) { + lz_stream *s; + int sucess; + + s = lzstream_check(L, lua_upvalueindex(1), LZ_INFLATE); + sucess = lz_read_line(L, s); + + if (s->error != Z_OK) { + return lz_pushresult(L, s); + } + + if (sucess) { + return 1; + } else { + /* EOF */ + return 0; + } +} + +static int lzstream_lines(lua_State *L) { + lzstream_check(L, 1, LZ_INFLATE); + lua_settop(L, 1); + lua_pushcclosure(L, lzstream_readline, 1); + return 1; +} + +/* ====================================================================== */ + +static int lzstream_docompress(lua_State *L, lz_stream *s, int from, int to, int flush) { + int r, arg; + int self = 0; + size_t b_size = s->o_buffer_max; + unsigned char *b = (unsigned char *)s->o_buffer; + + /* number of processed bytes */ + lua_rawgeti(L, LUA_REGISTRYINDEX, s->io_cb); + if (!lua_isfunction(L, -1)) { + self = 1; + lua_getfield(L, -1, "write"); + } + + for (arg = from; arg <= to; arg++) { + s->zstream.next_in = (unsigned char*)luaL_checklstring(L, arg, (size_t*)&s->zstream.avail_in); + + do { + s->zstream.next_out = b; + s->zstream.avail_out = b_size; + + /* bake some more */ + r = deflate(&s->zstream, flush); + if (r != Z_OK && r != Z_STREAM_END && r != Z_BUF_ERROR) { + lzstream_cleanup(L, s); + lua_pushboolean(L, 0); + lua_pushfstring(L, "failed to compress [%d]", r); + return 2; + } + + if (s->zstream.avail_out != b_size) { + /* write output */ + lua_pushvalue(L, -1); /* function */ + if (self) lua_pushvalue(L, -3); /* self */ + lua_pushlstring(L, (char*)b, b_size - s->zstream.avail_out); /* data */ + lua_call(L, (self ? 2 : 1), 0); + } + + if (r == Z_STREAM_END) { + lzstream_cleanup(L, s); + break; + } + + /* process all input */ + } while (s->zstream.avail_in > 0 || s->zstream.avail_out == 0); + } + + lua_pushboolean(L, 1); + return 1; +} + +static int lzstream_compress(lua_State *L) { + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + return lzstream_docompress(L, s, 2, lua_gettop(L), Z_NO_FLUSH); +} + + +/* ====================================================================== */ + +static int lzstream_flush(lua_State *L) { + static int flush_values[] = { Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH }; + static const char *const flush_opts[] = { "sync", "full", "finish" }; + + lz_stream *s = lzstream_check(L, 1, LZ_DEFLATE); + int flush = luaL_checkoption(L, 2, flush_opts[0], flush_opts); + + lua_settop(L, 0); + lua_pushliteral(L, ""); + return lzstream_docompress(L, s, 1, 1, flush_values[flush]); +} + +/* +** ========================================================================= +** zlib functions +** ========================================================================= +*/ + +static int lzlib_version(lua_State *L) +{ + lua_pushstring(L, zlibVersion()); + return 1; +} + +/* ====================================================================== */ +static int lzlib_adler32(lua_State *L) +{ + if (lua_gettop(L) == 0) + { + /* adler32 initial value */ + lua_pushnumber(L, adler32(0L, Z_NULL, 0)); + } + else + { + /* update adler32 checksum */ + size_t len; + int adler = (int) luaL_checkinteger(L, 1); + const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); + + lua_pushnumber(L, adler32(adler, buf, len)); + } + return 1; +} + +/* ====================================================================== */ +static int lzlib_crc32(lua_State *L) +{ + if (lua_gettop(L) == 0) + { + /* crc32 initial value */ + lua_pushnumber(L, crc32(0L, Z_NULL, 0)); + } + else + { + /* update crc32 checksum */ + size_t len; + int crc = (int) luaL_checkinteger(L, 1); + const unsigned char* buf = (unsigned char*)luaL_checklstring(L, 2, &len); + + lua_pushnumber(L, crc32(crc, buf, len)); + } + return 1; +} + +/* ====================================================================== */ + + +static int lzlib_compress(lua_State *L) { + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int level = (int) luaL_optinteger(L, 2, Z_DEFAULT_COMPRESSION); + int method = (int) luaL_optinteger(L, 3, Z_DEFLATED); + int windowBits = (int) luaL_optinteger(L, 4, 15); + int memLevel = (int) luaL_optinteger(L, 5, 8); + int strategy = (int) luaL_optinteger(L, 6, Z_DEFAULT_STRATEGY); + + int ret; + luaL_Buffer b; + z_stream zs; + + luaL_buffinit(L, &b); + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + zs.next_out = Z_NULL; + zs.avail_out = 0; + zs.next_in = Z_NULL; + zs.avail_in = 0; + + ret = deflateInit2(&zs, level, method, windowBits, memLevel, strategy); + + if (ret != Z_OK) + { + lua_pushnil(L); + lua_pushnumber(L, ret); + return 2; + } + + zs.next_in = (unsigned char*)next_in; + zs.avail_in = avail_in; + + for(;;) + { + zs.next_out = (unsigned char*)luaL_prepbuffer(&b); + zs.avail_out = LUAL_BUFFERSIZE; + + /* munch some more */ + ret = deflate(&zs, Z_FINISH); + + /* push gathered data */ + luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); + + /* done processing? */ + if (ret == Z_STREAM_END) + break; + + /* error condition? */ + if (ret != Z_OK) + break; + } + + /* cleanup */ + deflateEnd(&zs); + + luaL_pushresult(&b); + lua_pushnumber(L, ret); + return 2; +} + +/* ====================================================================== */ + +static int lzlib_decompress(lua_State *L) +{ + size_t avail_in; + const char *next_in = luaL_checklstring(L, 1, &avail_in); + int windowBits = (int) luaL_optinteger(L, 2, 15); + + int ret; + luaL_Buffer b; + z_stream zs; + + luaL_buffinit(L, &b); + + zs.zalloc = Z_NULL; + zs.zfree = Z_NULL; + + zs.next_out = Z_NULL; + zs.avail_out = 0; + zs.next_in = Z_NULL; + zs.avail_in = 0; + + ret = inflateInit2(&zs, windowBits); + + if (ret != Z_OK) { + lua_pushliteral(L, "failed to initialize zstream structures"); + lua_error(L); + } + + zs.next_in = (unsigned char*)next_in; + zs.avail_in = avail_in; + + for (;;) { + zs.next_out = (unsigned char*)luaL_prepbuffer(&b); + zs.avail_out = LUAL_BUFFERSIZE; + + /* bake some more */ + ret = inflate(&zs, Z_FINISH); + + /* push gathered data */ + luaL_addsize(&b, LUAL_BUFFERSIZE - zs.avail_out); + + /* done processing? */ + if (ret == Z_STREAM_END) + break; + + if (ret != Z_OK && ret != Z_BUF_ERROR) { + /* cleanup */ + inflateEnd(&zs); + + lua_pushliteral(L, "failed to process zlib stream"); + lua_error(L); + } + } + + /* cleanup */ + inflateEnd(&zs); + + luaL_pushresult(&b); + return 1; +} + + +/* +** ========================================================================= +** Register functions +** ========================================================================= +*/ + +#if (LUA_VERSION_NUM >= 502) + +#define luaL_register(L,n,f) luaL_setfuncs(L,f,0) + +#endif + +LUALIB_API int luaopen_zlib(lua_State *L) +{ + const luaL_Reg lzstream_meta[] = + { + {"write", lzstream_compress }, + {"read", lzstream_decompress }, + {"lines", lzstream_lines }, + {"flush", lzstream_flush }, + {"close", lzstream_close }, + + {"adler", lzstream_adler }, + + {"__tostring", lzstream_tostring }, + {"__gc", lzstream_gc }, + {NULL, NULL} + }; + + const luaL_Reg zlib[] = + { + {"version", lzlib_version }, + {"adler32", lzlib_adler32 }, + {"crc32", lzlib_crc32 }, + + {"deflate", lzlib_deflate }, + {"inflate", lzlib_inflate }, + + {"compress", lzlib_compress }, + {"decompress", lzlib_decompress }, + + {NULL, NULL} + }; + + /* ====================================================================== */ + + /* create new metatable for zlib compression structures */ + luaL_newmetatable(L, ZSTREAMMETA); + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -2); /* push metatable */ + lua_rawset(L, -3); /* metatable.__index = metatable */ + + /* + ** Stack: metatable + */ + luaL_register(L, NULL, lzstream_meta); + + lua_pop(L, 1); /* remove metatable from stack */ + + /* + ** Stack: + */ + lua_newtable(L); + + lua_pushliteral (L, "_COPYRIGHT"); + lua_pushliteral (L, "Copyright (C) 2003-2010 Tiago Dionizio"); + lua_settable (L, -3); + lua_pushliteral (L, "_DESCRIPTION"); + lua_pushliteral (L, "Lua 5 interface to access zlib library functions"); + lua_settable (L, -3); + lua_pushliteral (L, "_VERSION"); + lua_pushliteral (L, "lzlib 0.4-work3"); + lua_settable (L, -3); + +#define PUSH_LITERAL(name) \ + lua_pushliteral (L, #name); \ + lua_pushinteger (L, Z_##name); \ + lua_settable (L, -3); + +#define PUSH_NUMBER(name, value) \ + lua_pushliteral (L, #name); \ + lua_pushinteger (L, value); \ + lua_settable (L, -3); + + PUSH_LITERAL(NO_COMPRESSION) + PUSH_LITERAL(BEST_SPEED) + PUSH_LITERAL(BEST_COMPRESSION) + PUSH_LITERAL(DEFAULT_COMPRESSION) + + PUSH_LITERAL(FILTERED) + PUSH_LITERAL(HUFFMAN_ONLY) + PUSH_LITERAL(RLE) + PUSH_LITERAL(FIXED) + PUSH_LITERAL(DEFAULT_STRATEGY) + + PUSH_NUMBER(MINIMUM_MEMLEVEL, 1) + PUSH_NUMBER(MAXIMUM_MEMLEVEL, 9) + PUSH_NUMBER(DEFAULT_MEMLEVEL, 8) + + PUSH_NUMBER(DEFAULT_WINDOWBITS, 15) + PUSH_NUMBER(MINIMUM_WINDOWBITS, 8) + PUSH_NUMBER(MAXIMUM_WINDOWBITS, 15) + + PUSH_NUMBER(GZIP_WINDOWBITS, 16) + PUSH_NUMBER(RAW_WINDOWBITS, -1) + + luaL_register(L, NULL, zlib); + + /* + ** Stack: zlib table + */ + return 1; +} diff --git a/nse_zlib.h b/nse_zlib.h new file mode 100644 index 0000000000..624da87091 --- /dev/null +++ b/nse_zlib.h @@ -0,0 +1,9 @@ +#ifndef ZLIB +#define ZLIB + +#define NSE_ZLIBNAME "zlib" + +LUALIB_API int luaopen_zlib(lua_State *L); + +#endif + diff --git a/nselib/unittest.lua b/nselib/unittest.lua index 5c58c3683f..faeb5cab6a 100644 --- a/nselib/unittest.lua +++ b/nselib/unittest.lua @@ -153,6 +153,7 @@ local libs = { "wsdd", "xdmcp", "xmpp", +"zlib", } -- This script-arg is documented in the unittest script to avoid cluttering diff --git a/nselib/zlib.luadoc b/nselib/zlib.luadoc new file mode 100644 index 0000000000..2148896bce --- /dev/null +++ b/nselib/zlib.luadoc @@ -0,0 +1,111 @@ +--- +-- Zlib compression and decompression library +-- +-- From https://github.com/LuaDist/lzlib +-- @author Tiago Dionizio +-- @copyright The MIT License (MIT); Copyright Tiago Dionizio (tiago.dionizio@gmail.com) +module "zlib" + +-- NSEdoc derived from lzlib's README + +--- returns zlib version +-- @return the zlib version +function version() + +--- Compute an adler32 checksum +-- +-- Without any parameters, returns the initial adler32 value. +-- +-- Call to update the adler32 value, adler is the current value, buffer is passed +-- to adler32 zlib function and the updated value is returned. +-- @param adler An integer, the result of a previous call to adler32 +-- @param buffer A string, over which to compute the adler32 checksum. +-- @return The adler32 checksum result. +function adler32(adler, buffer) + +--- Compute a crc32 checksum +-- +-- Without any parameters, returns the initial crc32 value. +-- +-- Call to update the crc32 value, crc is the current value, buffer is passed +-- to crc32 zlib function and the updated value is returned. +-- @param crc An integer, the result of a previous call to crc32 +-- @param buffer A string, over which to compute the crc32 checksum. +-- @return The crc32 checksum result. +function crc32(crc, buffer) + +--- Return a string containing the compressed buffer according to the given parameters. +-- +-- All of the parameters besides buffer are optional. The zlib library sets the +-- default values, which are usually equivalent to +-- compress(buffer, 6, Z_DEFLATED, 15, 8, Z_DEFAULT_STRATEGY) +-- @param buffer String to compress +-- @param level Optional integer corresponding to compression level, 0-9 +-- @param method Optional integer corresponding to compression method +-- @param windowBits Optional integer, the base-2 logarithm of the maximum window size. Default: 15 +-- @param memLevel Optional integer corresponding to memory use and therefore speed +-- @param strategy Optional integer corresponding to compression strategy +function compress(buffer, level, method, windowBits, memLevel, strategy) + +--- Return the decompressed stream after processing the given buffer. +-- +-- If windowBits is negative, this function decompresses raw deflate data without header. +-- @param buffer String containing DEFLATE-compressed data +-- @param windowBits Optional integer, the base-2 logarithm of the maximum window size. Default: 15 +function decompress(buffer, windowBits) + +--- Return a deflate stream. +-- +-- This stream is a file-like object that can be used to write compressed +-- (deflated) data to some sink stream. If the sink stream is a table or +-- object, it must have a write method. Otherwise, it must be a +-- function that will be called with data to write (e.g. +-- socket.send). +-- @param sink The location where compressed data will be written +-- @param level Optional integer corresponding to compression level, 0-9 +-- @param method Optional integer corresponding to compression method +-- @param windowBits Optional integer, the base-2 logarithm of the maximum window size. Default: 15 +-- @param memLevel Optional integer corresponding to memory use and therefore speed +-- @param strategy Optional integer corresponding to compression strategy +-- @param dictionary Optional string corresponding to initial compression dictionary +function deflate(sink, level, method, windowBits, memLevel, strategy, dictionary) + +--- Read from an inflate stream +-- +-- @param how A number of bytes to read, or "*l" (read until newline), or "*a" (read the whole stream). Default: "*l" +-- @return The bytes read, or nil on EOF. +function inflate_stream:read(how) + +--- Return iterator that returns a line each time it is called, or nil on end +-- of file. +-- @return An iterator suitable for use with for +function inflate_stream:lines() + +--- Close the stream. +--@return True, or false on error +--@return An error string +function stream:close() + +--- Return an inflate stream. +-- +-- This stream is a file-like object that can be used to read decompressed +-- (inflated) data from some source stream. If the source stream is a table or +-- object, it must have a read method. Otherwise, it must be a +-- function that will be called to get more compressed data. +-- @param source The location where compressed data will be read from +-- @param windowBits Optional integer, the base-2 logarithm of the maximum window size. Default: 15 +-- @param dictionary Optional string corresponding to initial compression dictionary +-- @return an inflate stream. +function inflate(source, windowBits, dictionary) + +--- Write each parameter into the stream +-- @param ... any number of strings to write +-- @return True, or false on error +-- @return Error string on error +function deflate_stream:write(...) + +--- Flush output for deflate streams. +-- @param value One of 'sync', 'full', or 'finish' +-- @return True, or false on error +-- @return Error string on error +function deflate_stream:flush(value)