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)