From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- nse_main.cc | 857 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 857 insertions(+) create mode 100644 nse_main.cc (limited to 'nse_main.cc') diff --git a/nse_main.cc b/nse_main.cc new file mode 100644 index 0000000..2382688 --- /dev/null +++ b/nse_main.cc @@ -0,0 +1,857 @@ +#include "nmap.h" +#include "nbase.h" +#include "nmap_error.h" +#include "portlist.h" +#include "nsock.h" +#include "NmapOps.h" +#include "timing.h" +#include "Target.h" +#include "nmap_tty.h" +#include "xml.h" + +#include "nse_main.h" +#include "nse_utility.h" +#include "nse_db.h" +#include "nse_fs.h" +#include "nse_nsock.h" +#include "nse_nmaplib.h" +#include "nse_openssl.h" +#include "nse_debug.h" +#include "nse_lpeg.h" +#include "nse_libssh2.h" +#include "nse_zlib.h" + +#include + +#define NSE_MAIN "NSE_MAIN" /* the main function */ + +/* Script Scan phases */ +#define NSE_PRE_SCAN "NSE_PRE_SCAN" +#define NSE_SCAN "NSE_SCAN" +#define NSE_POST_SCAN "NSE_POST_SCAN" + +/* These are indices into the registry, for data shared with nse_main.lua. The + definitions here must match those in nse_main.lua. */ +#define NSE_YIELD "NSE_YIELD" +#define NSE_BASE "NSE_BASE" +#define NSE_WAITING_TO_RUNNING "NSE_WAITING_TO_RUNNING" +#define NSE_DESTRUCTOR "NSE_DESTRUCTOR" +#define NSE_SELECTED_BY_NAME "NSE_SELECTED_BY_NAME" +#define NSE_CURRENT_HOSTS "NSE_CURRENT_HOSTS" + +#define NSE_FORMAT_TABLE "NSE_FORMAT_TABLE" +#define NSE_FORMAT_XML "NSE_FORMAT_XML" +#define NSE_PARALLELISM "NSE_PARALLELISM" + +#ifndef MAXPATHLEN +# define MAXPATHLEN 2048 +#endif + +extern NmapOps o; + +/* global object to store Pre-Scan and Post-Scan script results */ +static ScriptResults script_scan_results; + +static int timedOut (lua_State *L) +{ + Target *target = nseU_gettarget(L, 1); + lua_pushboolean(L, target->timedOut(NULL)); + return 1; +} + +static int startTimeOutClock (lua_State *L) +{ + Target *target = nseU_gettarget(L, 1); + if (!target->timeOutClockRunning()) + target->startTimeOutClock(NULL); + return 0; +} + +static int stopTimeOutClock (lua_State *L) +{ + Target *target = nseU_gettarget(L, 1); + if (target->timeOutClockRunning()) + target->stopTimeOutClock(NULL); + return 0; +} + +static int next_port (lua_State *L) +{ + lua_settop(L, 2); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_pushvalue(L, 2); + if (lua_next(L, -2) == 0) + return 0; + else { + lua_pop(L, 1); /* pop boolean value */ + return 1; + } +} + +static int ports (lua_State *L) +{ + static const int states[] = { + PORT_OPEN, + PORT_OPENFILTERED, + PORT_UNFILTERED, + PORT_HIGHEST_STATE /* last one marks end */ + }; + Target *target = nseU_gettarget(L, 1); + PortList *plist = &(target->ports); + Port *current = NULL; + Port port; + lua_newtable(L); + for (int i = 0; states[i] != PORT_HIGHEST_STATE; i++) + while ((current = plist->nextPort(current, &port, TCPANDUDPANDSCTP, + states[i])) != NULL) + { + lua_createtable(L, 0, NSE_NUM_PORTINFO_FIELDS); + set_portinfo(L, target, current); + lua_pushboolean(L, 1); + lua_rawset(L, -3); + } + lua_pushcclosure(L, next_port, 1); + lua_pushnil(L); + lua_pushnil(L); + return 3; +} + +static int script_set_output (lua_State *L) +{ + ScriptResult *sr = new ScriptResult; + sr->set_output_tab(L, 1); + script_scan_results.insert(sr); + return 0; +} + +static int host_set_output (lua_State *L) +{ + ScriptResult *sr = new ScriptResult; + Target *target = nseU_gettarget(L, 1); + sr->set_output_tab(L, 2); + target->scriptResults.insert(sr); + return 0; +} + +static int port_set_output (lua_State *L) +{ + Port *p; + Port port; + ScriptResult *sr = new ScriptResult; + Target *target = nseU_gettarget(L, 1); + p = nseU_getport(L, target, &port, 2); + sr->set_output_tab(L, 3); + target->ports.addScriptResult(p->portno, p->proto, sr); + target->ports.numscriptresults++; + return 0; +} + +static int key_was_pressed (lua_State *L) +{ + lua_pushboolean(L, keyWasPressed()); + return 1; +} + +static int scp (lua_State *L) +{ + static const char * const ops[] = {"printStats", "printStatsIfNecessary", + "mayBePrinted", "endTask", NULL}; + ScanProgressMeter *progress = + (ScanProgressMeter *) lua_touserdata(L, lua_upvalueindex(1)); + switch (luaL_checkoption(L, 1, NULL, ops)) + { + case 0: /* printStats */ + progress->printStats((double) luaL_checknumber(L, 2), NULL); + break; + case 1: + progress->printStatsIfNecessary((double) luaL_checknumber(L, 2), NULL); + break; + case 2: /*mayBePrinted */ + lua_pushboolean(L, progress->mayBePrinted(NULL)); + return 1; + case 3: /* endTask */ + progress->endTask(NULL, NULL); + delete progress; + break; + } + return 0; +} + +static int scan_progress_meter (lua_State *L) +{ + lua_pushlightuserdata(L, new ScanProgressMeter(luaL_checkstring(L, 1))); + lua_pushcclosure(L, scp, 1); + return 1; +} + +/* This is like nmap.log_write, but doesn't append "NSE:" to the beginning of + messages. It is only used internally by nse_main.lua and is not available to + scripts. */ +static int l_log_write(lua_State *L) +{ + static const char *const ops[] = {"stdout", "stderr", NULL}; + static const int logs[] = {LOG_STDOUT, LOG_STDERR}; + int log = logs[luaL_checkoption(L, 1, NULL, ops)]; + log_write(log, "%s", luaL_checkstring(L, 2)); + return 0; +} + +static int l_xml_start_tag(lua_State *L) +{ + const char *name; + + name = luaL_checkstring(L, 1); + xml_open_start_tag(name); + + if (lua_isnoneornil(L, 2)) { + lua_newtable(L); + lua_replace(L, 2); + } + + for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) + xml_attribute(luaL_checkstring(L, -2), "%s", luaL_checkstring(L, -1)); + + xml_close_start_tag(); + + return 0; +} + +static int l_xml_end_tag(lua_State *L) +{ + xml_end_tag(); + + return 0; +} + +static int l_xml_write_escaped(lua_State *L) +{ + const char *text; + + text = luaL_checkstring(L, 1); + xml_write_escaped("%s", text); + + return 0; +} + +static int l_xml_newline(lua_State *L) +{ + xml_newline(); + + return 0; +} + +static int l_protect_xml(lua_State *L) +{ + const char *text; + size_t len; + std::string output; + + text = luaL_checklstring(L, 1, &len); + output = protect_xml(std::string(text, len)); + lua_pushlstring(L, output.c_str(), output.size()); + + return 1; +} + +static int nse_fetch (lua_State *L, int (*fetch)(char *, size_t, const char *)) +{ + char path[MAXPATHLEN]; + const char *input = luaL_checkstring(L, 1); + switch (fetch(path, sizeof(path), input)) + { + case 0: // no such path + lua_pushnil(L); + lua_pushfstring(L, "no path to file/directory: %s", lua_tostring(L, 1)); + break; + case 1: // file returned + lua_pushliteral(L, "file"); + lua_pushstring(L, path); + break; + case 2: // directory returned + if (input[strlen(input) - 1] == '/') { + lua_pushliteral(L, "directory"); + } + else { + lua_pushliteral(L, "bare_directory"); + } + lua_pushstring(L, path); + break; + default: + return luaL_error(L, "nse_fetch returned bad code"); + } + return 2; +} + +static bool filename_is_absolute(const char *file) { + if (file[0] == '/') + return true; +#ifdef WIN32 + if ((file[0] != '\0' && file[1] == ':') || file[0] == '\\') + return true; +#endif + return false; +} + +/* This is a modification of nmap_fetchfile that first looks for an + * absolute file name. + */ +static int nse_fetchfile_absolute(char *path, size_t path_len, const char *file) { + if (filename_is_absolute(file)) { + if (o.debugging > 1) + log_write(LOG_STDOUT, "%s: Trying absolute path %s\n", SCRIPT_ENGINE, file); + Strncpy(path, file, path_len); + return file_is_readable(file); + } + + return nmap_fetchfile(path, path_len, file); +} + +/* This is a modification of nmap_fetchfile specialized to look for files + * in the scripts subdirectory. If the path is absolute, it is always tried + * verbatim. Otherwise, the file is looked for under scripts/, and then finally + * in the current directory. + */ +static int nse_fetchscript(char *path, size_t path_len, const char *file) { + std::string scripts_path = std::string(SCRIPT_ENGINE_LUA_DIR) + std::string(file); + int type; + + if (filename_is_absolute(file)) { + if (o.debugging > 1) + log_write(LOG_STDOUT, "%s: Trying absolute path %s\n", SCRIPT_ENGINE, file); + Strncpy(path, file, path_len); + return file_is_readable(file); + } + + // lets look in /scripts + type = nmap_fetchfile(path, path_len, scripts_path.c_str()); + + if (type == 0) { + // current directory + Strncpy(path, file, path_len); + return file_is_readable(file); + } + + return type; +} + +static int fetchscript (lua_State *L) +{ + return nse_fetch(L, nse_fetchscript); +} + +static int fetchfile_absolute (lua_State *L) +{ + return nse_fetch(L, nse_fetchfile_absolute); +} + +static void open_cnse (lua_State *L) +{ + static const luaL_Reg nse[] = { + {"fetchfile_absolute", fetchfile_absolute}, + {"fetchscript", fetchscript}, + {"key_was_pressed", key_was_pressed}, + {"scan_progress_meter", scan_progress_meter}, + {"timedOut", timedOut}, + {"startTimeOutClock", startTimeOutClock}, + {"stopTimeOutClock", stopTimeOutClock}, + {"ports", ports}, + {"script_set_output", script_set_output}, + {"host_set_output", host_set_output}, + {"port_set_output", port_set_output}, + {"log_write", l_log_write}, + {"xml_start_tag", l_xml_start_tag}, + {"xml_end_tag", l_xml_end_tag}, + {"xml_write_escaped", l_xml_write_escaped}, + {"xml_newline", l_xml_newline}, + {"protect_xml", l_protect_xml}, + {NULL, NULL} + }; + + luaL_newlib(L, nse); + /* Add some other fields */ + nseU_setbfield(L, -1, "default", o.script); + nseU_setbfield(L, -1, "scriptversion", o.scriptversion); + nseU_setbfield(L, -1, "scriptupdatedb", o.scriptupdatedb); + nseU_setbfield(L, -1, "scripthelp", o.scripthelp); + nseU_setsfield(L, -1, "script_dbpath", SCRIPT_ENGINE_DATABASE); + nseU_setsfield(L, -1, "scriptargs", o.scriptargs); + nseU_setsfield(L, -1, "scriptargsfile", o.scriptargsfile); + nseU_setsfield(L, -1, "NMAP_URL", NMAP_URL); + nseU_setnfield(L, -1, "script_timeout", o.scripttimeout); + +} + +/* Global persistent Lua state used by the engine. */ +static lua_State *L_NSE = NULL; + +void ScriptResult::clear (void) +{ + if (o.debugging > 3) + log_write(LOG_STDOUT, "ScriptResult::clear %d id %s\n", output_ref, get_id()); + luaL_unref(L_NSE, LUA_REGISTRYINDEX, output_ref); + output_ref = LUA_NOREF; + id = NULL; +} + +void ScriptResult::set_output_tab (lua_State *L, int base) +{ + assert(lua_gettop(L) - base == 2); // we must have 3 args + id = luaL_checkstring(L, base); + lua_newtable(L); + lua_insert(L, base); + + // string output + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + } + else { + if (!lua_isstring(L, -1)) { + luaL_error(L, "String output is not a string"); + return; + } + lua_setfield(L, base, "str"); + } + + // structured output + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + } + else { + lua_setfield(L, base, "tab"); + } + + // script id. We copy it here to ensure ScriptResult::id stays valid + // (i.e. not garbage-collected) + if (!lua_isstring(L, -1)) { + luaL_error(L, "Script ID is not a string"); + return; + } + lua_setfield(L, base, "id"); + assert(lua_gettop(L) == base); + output_ref = luaL_ref(L_NSE, LUA_REGISTRYINDEX); + if (o.debugging > 3) + log_write(LOG_STDOUT, "ScriptResult::set_output_tab %d id %s\n", output_ref, get_id()); +} + +static void format_obj(lua_State *L, int pos, std::string &output) +{ + pos = lua_absindex(L, pos); + + /* Look up the FORMAT_TABLE function from nse_main.lua and call it. */ + lua_getfield(L, LUA_REGISTRYINDEX, NSE_FORMAT_TABLE); + if (lua_isnil(L, -1)) { + log_write(LOG_STDOUT, "%s: Cannot find function _R[\"%s\"] that should be in nse_main.lua\n", + SCRIPT_ENGINE, NSE_FORMAT_TABLE); + lua_pop(L, 1); + return; + } + + lua_pushvalue(L, pos); + if (lua_pcall(L, 1, 1, 0) != 0) { + if (o.debugging) + log_write(LOG_STDOUT, "%s: Error in FORMAT_TABLE: %s\n", SCRIPT_ENGINE, lua_tostring(L, -1)); + lua_pop(L, 1); + return; + } + + size_t len = 0; + const char *str = lua_tolstring(L, -1, &len); + output.assign(str, len); + lua_pop(L, 1); +} + +std::string ScriptResult::get_output_str (void) const +{ + std::string output; + + assert(output_ref != LUA_NOREF); + + int out_obj = lua_rawgeti(L_NSE, LUA_REGISTRYINDEX, output_ref); + assert(out_obj == LUA_TTABLE); + + /* Explicit string output? */ + if (LUA_TSTRING == lua_getfield(L_NSE, -1, "str")) { + size_t len = 0; + const char *str = lua_tolstring(L_NSE, -1, &len); + output.assign(str, len); + } + else { + lua_pop(L_NSE, 1); // get rid of whatever that was (nil) + /* Auto-formatted table output? */ + if (LUA_TNIL != lua_getfield(L_NSE, -1, "tab")) + format_obj(L_NSE, -1, output); + } + + lua_pop(L_NSE, 2); + return output; +} + +ScriptResults *get_script_scan_results_obj (void) +{ + return &script_scan_results; +} + +static void format_xml(lua_State *L, int pos) +{ + pos = lua_absindex(L, pos); + + /* Look up the FORMAT_XML function from nse_main.lua and call it. */ + lua_getfield(L, LUA_REGISTRYINDEX, NSE_FORMAT_XML); + if (lua_isnil(L, -1)) { + log_write(LOG_STDOUT, "%s: Cannot find function _R[\"%s\"] that should be in nse_main.lua\n", + SCRIPT_ENGINE, NSE_FORMAT_XML); + lua_pop(L, 1); + return; + } + + lua_pushvalue(L, pos); + if (lua_pcall(L, 1, 1, 0) != 0) { + if (o.debugging) + log_write(LOG_STDOUT, "%s: Error in FORMAT_XML: %s\n", SCRIPT_ENGINE, lua_tostring(L, -1)); + lua_pop(L, 1); + return; + } +} + +void ScriptResult::write_xml() const +{ + std::string output_str; + assert(output_ref != LUA_NOREF); + assert(id != NULL); + + int out_obj = lua_rawgeti(L_NSE, LUA_REGISTRYINDEX, output_ref); + assert(out_obj == LUA_TTABLE); + + out_obj = lua_gettop(L_NSE); + + xml_open_start_tag("script"); + xml_attribute("id", "%s", get_id()); + + output_str = get_output_str(); + if (!output_str.empty()) + xml_attribute("output", "%s", protect_xml(output_str).c_str()); + else { + error("Bug in %s: no string output.", get_id()); + xml_attribute("output", "%s", ""); + } + + /* Any table output? */ + if (LUA_TNIL != lua_getfield(L_NSE, out_obj, "tab")) { + xml_close_start_tag(); + format_xml(L_NSE, -1); + xml_end_tag(); + } else { + xml_close_empty_tag(); + } + + lua_settop(L_NSE, out_obj - 1); +} + +/* int panic (lua_State *L) + * + * Panic function set via lua_atpanic(). + */ +static int panic (lua_State *L) +{ + const char *err = lua_tostring(L, 1); + fatal("Unprotected error in Lua:\n%s\n", err); + return 0; +} + +static void set_nmap_libraries (lua_State *L) +{ + static const luaL_Reg libs[] = { + {NSE_NMAPLIBNAME, luaopen_nmap}, + {NSE_DBLIBNAME, luaopen_db}, + {LFSLIBNAME, luaopen_lfs}, + {LPEGLIBNAME, luaopen_lpeg}, +#ifdef HAVE_LIBSSH2 + {LIBSSH2LIBNAME, luaopen_libssh2}, +#endif +#ifdef HAVE_OPENSSL + {OPENSSLLIBNAME, luaopen_openssl}, +#endif +#ifdef HAVE_LIBZ + {NSE_ZLIBNAME, luaopen_zlib}, +#endif + {NULL, NULL} + }; + + for (int i = 0; libs[i].name; i++) { + luaL_requiref(L, libs[i].name, libs[i].func, 1); + lua_pop(L, 1); + } +} + +static int init_main (lua_State *L) +{ + char path[MAXPATHLEN]; + std::vector *rules = (std::vector *) + lua_touserdata(L, 1); + + /* Load some basic libraries */ + luaL_openlibs(L); + set_nmap_libraries(L); + + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, NSE_CURRENT_HOSTS); + + if (nmap_fetchfile(path, sizeof(path), "nse_main.lua") != 1) + luaL_error(L, "could not locate nse_main.lua"); + if (luaL_loadfile(L, path) != 0) + luaL_error(L, "could not load nse_main.lua: %s", lua_tostring(L, -1)); + + /* The first argument to the NSE Main Lua code is the private nse + * library table which exposes certain necessary C functions to + * the Lua engine. + */ + open_cnse(L); /* first argument */ + + /* The second argument is the script rules, including the + * files/directories/categories passed as the userdata to this function. + */ + lua_createtable(L, rules->size(), 0); /* second argument */ + for (std::vector::iterator si = rules->begin(); si != rules->end(); si++) + nseU_appendfstr(L, -1, "%s", si->c_str()); + + lua_call(L, 2, 1); /* returns the NSE main function */ + + lua_setfield(L, LUA_REGISTRYINDEX, NSE_MAIN); + + lua_pushinteger(L, o.min_parallelism); + lua_setfield(L, LUA_REGISTRYINDEX, NSE_PARALLELISM); + + return 0; +} + +static int run_main (lua_State *L) +{ + std::vector *targets = (std::vector *) + lua_touserdata(L, 1); + + /* New host group */ + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, NSE_CURRENT_HOSTS); + + lua_getfield(L, LUA_REGISTRYINDEX, NSE_MAIN); + assert(lua_isfunction(L, -1)); + + /* The first argument to the NSE main function is the list of targets. This + * has all the target names, 1-N, in a list. + */ + lua_createtable(L, targets->size(), 0); + int targets_table = lua_gettop(L); + lua_getfield(L, LUA_REGISTRYINDEX, NSE_CURRENT_HOSTS); + int current_hosts = lua_gettop(L); + for (std::vector::iterator ti = targets->begin(); ti != targets->end(); ti++) + { + Target *target = (Target *) *ti; + if (target->timedOut(NULL)) { + continue; + } + const char *TargetName = target->TargetName(); + const char *targetipstr = target->targetipstr(); + lua_newtable(L); + set_hostinfo(L, target); + lua_rawseti(L, targets_table, lua_rawlen(L, targets_table) + 1); + /* Index this Target in NSE_CURRENT_HOSTS under targetname and IP so we can + * retrieve it later */ + if (TargetName != NULL && strcmp(TargetName, "") != 0) { + lua_pushstring(L, TargetName); + lua_pushlightuserdata(L, target); + lua_rawset(L, current_hosts); /* add to NSE_CURRENT_HOSTS */ + } + lua_pushstring(L, targetipstr); + lua_pushlightuserdata(L, target); + lua_rawset(L, current_hosts); /* add to NSE_CURRENT_HOSTS */ + } + lua_settop(L, targets_table); + + /* Push script scan phase type. Second argument to NSE main function */ + switch (o.current_scantype) + { + case SCRIPT_PRE_SCAN: + lua_pushliteral(L, NSE_PRE_SCAN); + break; + case SCRIPT_SCAN: + lua_pushliteral(L, NSE_SCAN); + break; + case SCRIPT_POST_SCAN: + lua_pushliteral(L, NSE_POST_SCAN); + break; + default: + fatal("%s: failed to set the script scan phase.\n", SCRIPT_ENGINE); + } + + lua_call(L, 2, 0); + + return 0; +} + +/* int nse_yield (lua_State *L, int ctx, lua_CFunction k) [-?, +?, e] + * + * This function will yield the running thread back to NSE, even across script + * auxiliary coroutines. All NSE initiated yields must use this function. The + * correct and only way to call is as a tail call: + * return nse_yield(L, 0, NULL); + */ +int nse_yield (lua_State *L, lua_KContext ctx, lua_KFunction k) +{ + lua_getfield(L, LUA_REGISTRYINDEX, NSE_YIELD); + lua_pushthread(L); + lua_call(L, 1, 1); /* returns NSE_YIELD_VALUE */ + return lua_yieldk(L, 1, ctx, k); /* yield with NSE_YIELD_VALUE */ +} + +/* void nse_restore (lua_State *L, int number) [-, -, e] + * + * Restore the thread 'L' back into the running NSE queue. 'number' is the + * number of values on the stack to be passed when the thread is resumed. This + * function may cause a panic due to extraordinary and unavoidable + * circumstances. + */ +void nse_restore (lua_State *L, int number) +{ + luaL_checkstack(L, 5, "nse_restore: stack overflow"); + lua_pushthread(L); + lua_getfield(L, LUA_REGISTRYINDEX, NSE_WAITING_TO_RUNNING); + lua_insert(L, -(number+2)); /* move WAITING_TO_RUNNING down below the args */ + lua_insert(L, -(number+1)); /* move thread above WAITING_TO_RUNNING */ + /* Call WAITING_TO_RUNNING (defined in nse_main.lua) on the thread and any + other arguments. */ + if (lua_pcall(L, number+1, 0, 0) != 0) + fatal("%s: WAITING_TO_RUNNING error!\n%s", __func__, lua_tostring(L, -1)); +} + +/* void nse_destructor (lua_State *L, char what) [-(1|2), +0, e] + * + * This function adds (what = 'a') or removes (what = 'r') a destructor from + * the Thread owning the running Lua thread (L). A destructor is called when + * the thread finishes for any reason (including error). A unique key is used + * to associate with the destructor so it is removable later. + * + * what == 'r', destructor key on stack + * what == 'a', destructor key and destructor function on stack + */ +void nse_destructor (lua_State *L, char what) +{ + assert(what == 'a' || what == 'r'); + lua_getfield(L, LUA_REGISTRYINDEX, NSE_DESTRUCTOR); + lua_pushstring(L, what == 'a' ? "add" : "remove"); + lua_pushthread(L); + if (what == 'a') + { + lua_pushvalue(L, -5); /* destructor key */ + lua_pushvalue(L, -5); /* destructor */ + } + else + { + lua_pushvalue(L, -4); /* destructor key */ + lua_pushnil(L); /* no destructor, we are removing */ + } + if (lua_pcall(L, 4, 0, 0) != 0) + fatal("%s: NSE_DESTRUCTOR error!\n%s", __func__, lua_tostring(L, -1)); + lua_pop(L, what == 'a' ? 2 : 1); +} + +/* void nse_base (lua_State *L) [-0, +1, e] + * + * Returns the base Lua thread (coroutine) for the running thread. The base + * thread is resumed by NSE (runs the action function). Other coroutines being + * used by the base thread may be in a chain of resumes, we use the base thread + * as the "holder" of resources (for the Nsock binding in particular). + */ +void nse_base (lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, NSE_BASE); + lua_call(L, 0, 1); /* returns base thread */ +} + +/* void nse_selectedbyname (lua_State *L) [-0, +1, e] + * + * Returns a boolean signaling whether the running script was selected by name + * on the command line (--script). + */ +void nse_selectedbyname (lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, NSE_SELECTED_BY_NAME); + if (lua_isnil(L, -1)) { + lua_pushboolean(L, 0); + lua_replace(L, -2); + } else { + lua_call(L, 0, 1); + } +} + +/* void nse_gettarget (lua_State *L) [-0, +1, -] + * + * Given the index to a string on the stack identifying the host, an ip or a + * targetname (host name specified on the command line, see Target.h), returns + * a lightuserdatum that points to the host's Target (see Target.h). If the + * host cannot be found, nil is returned. + */ +void nse_gettarget (lua_State *L, int index) +{ + lua_pushvalue(L, index); + lua_getfield(L, LUA_REGISTRYINDEX, NSE_CURRENT_HOSTS); + lua_insert(L, -2); + lua_rawget(L, -2); + lua_replace(L, -2); +} + +void open_nse (void) +{ + if (L_NSE == NULL) + { + /* + Set the random seed value on behalf of scripts. Since Lua uses the + C rand and srand functions, which have a static seed for the entire + program, we don't want scripts doing this themselves. + */ + srand(get_random_uint()); + + const lua_Number version = lua_version(NULL); + double major = (version) / 100.0; + double minor = fmod(version, 10.0); + if (o.debugging >= 1) + log_write(LOG_STDOUT, "%s: Using Lua %.0f.%.0f.\n", SCRIPT_ENGINE, major, minor); + if (version < 504) + fatal("%s: This version of NSE only works with Lua 5.4 or greater.", SCRIPT_ENGINE); + if ((L_NSE = luaL_newstate()) == NULL) + fatal("%s: failed to open a Lua state!", SCRIPT_ENGINE); + lua_atpanic(L_NSE, panic); + lua_settop(L_NSE, 0); + + lua_pushcfunction(L_NSE, nseU_traceback); + lua_pushcfunction(L_NSE, init_main); + lua_pushlightuserdata(L_NSE, &o.chosenScripts); + if (lua_pcall(L_NSE, 1, 0, 1)) + fatal("%s: failed to initialize the script engine:\n%s\n", SCRIPT_ENGINE, lua_tostring(L_NSE, -1)); + lua_settop(L_NSE, 0); + } +} + +void script_scan (std::vector &targets, stype scantype) +{ + o.current_scantype = scantype; + + assert(L_NSE != NULL); + lua_settop(L_NSE, 0); /* clear the stack */ + + lua_pushcfunction(L_NSE, nseU_traceback); + lua_pushcfunction(L_NSE, run_main); + lua_pushlightuserdata(L_NSE, &targets); + if (lua_pcall(L_NSE, 1, 0, 1)) + error("%s: Script Engine Scan Aborted.\nAn error was thrown by the " + "engine: %s", SCRIPT_ENGINE, lua_tostring(L_NSE, -1)); + lua_settop(L_NSE, 0); +} + +void close_nse (void) +{ + if (L_NSE != NULL) + { + lua_close(L_NSE); + L_NSE = NULL; + } +} -- cgit v1.2.3