--- nmap-4.65/nse_main.cc 2008-05-30 20:39:27.000000000 -0600 +++ patrick/nse_main.cc 2008-06-03 01:50:14.000000000 -0600 @@ -1,17 +1,9 @@ #include "nse_main.h" - -extern "C" { - #include "lua.h" - #include "lualib.h" - #include "lauxlib.h" -} - #include "nse_init.h" #include "nse_nsock.h" #include "nse_nmaplib.h" #include "nse_debug.h" #include "nse_macros.h" -#include "nse_string.h" #include "nmap.h" #include "nmap_error.h" @@ -24,51 +16,108 @@ extern NmapOps o; -struct run_record { - short type; // 0 - hostrule; 1 - portrule - unsigned int index; // index in the corresponding table - Port* port; - Target* host; -}; - -struct thread_record { - lua_State* thread; - int resume_arguments; - unsigned int registry_idx; // index in the main state registry - double runlevel; - run_record* rr; -}; - int current_hosts = 0; +/* errfunc is an integer key in Lua's registry pointing to an error + * function set by init_lua. It's used to print a traceback when + * an error is encountered inside lua_pcall. + */ int errfunc = 0; -std::list > torun_scripts; -std::list running_scripts; -std::list waiting_scripts; - -class CompareRunlevels { -public: - bool operator() (const struct thread_record& lhs, const struct thread_record& rhs) { - return lhs.runlevel < rhs.runlevel; - } -}; - -// prior execution -int process_preparerunlevels(std::list torun_threads); -int process_preparehost(lua_State* L, Target* target, std::list& torun_threads); -int process_preparethread(lua_State* L, struct run_record rr, struct thread_record* tr); - -// helper functions -int process_getScriptId(lua_State* L, struct script_scan_result* ssr); -int process_pickScriptsForPort( - lua_State* L, - Target* target, - Port* port, - std::vector& torun); - -// execution -int process_mainloop(lua_State* L); -int process_waiting2running(lua_State* L, int resume_arguments); -int process_finalize(lua_State* L, unsigned int registry_idx); +/* nse_threads is an integer key in Lua's registry pointing to a + * table that holds information for each thread. It looks like this: + * { + * [Thread 1] = { + * host = host_table, + * runlevel = runlevel_number, + * type = type_number, + * target = target_userdata, + * id = id_string + * }, + * [Thread 2] = { + * host = host_table, + * port = port_table, + * runlevel = runlevel_number, + * type = type_number, + * target = target_userdata, + * id = id_string + * } + * } + * So it's keys are threads. Each has a table containing relevant information. + * This table has weak keys so when the thread is finished, these threads are + * collected normally along with their information. When the final thread for + * a target is collected, it's finalizer is called and it's host's time out + * clock is stopped. Currently this table isn't used anymore, but is left + * for convenience. + */ +int nse_threads = 0; +/* nse_runlevels is an integer key in Lua's registry pointing to a table + * that holds information for each runlevel. It has runlevel (double) keys + * with table values. These tables hold Thread keys and Thread Info table + * values. These Thread Info tables are the same tables in nse_threads. + * When mainloop is called, each runlevel table is passed to mainloop. + * This table becomes the "running threads". mainloop will create another + * table that holds all "waiting threads" (those blocked on an i/o operation). + * nse_runlevels looks like this: + * { + * [runlevel_double1] = { + * Thread_1 = { + * host = host_table, + * runlevel = runlevel_number, + * type = type_number, + * target = target_userdata, + * id = id_string + * }, + * Thread_2 = { + * host = host_table, + * runlevel = runlevel_number, + * type = type_number, + * target = target_userdata, + * id = id_string + * } + * }, + * [runlevel_double2] = { + * Thread_3 = { + * host = host_table, + * runlevel = runlevel_number, + * type = type_number, + * target = target_userdata, + * id = id_string + * }, + * Thread_4 = { + * host = host_table, + * runlevel = runlevel_number, + * type = type_number, + * target = target_userdata, + * id = id_string + * } + * } + * } + */ +int nse_runlevels = 0; +/* Below is a simple list which holds all Threads ready to be moved + * into the running threads array. These are added from the nsock library + * via the process_waiting2running() procedure. + */ +static std::list waiting; + +void ScriptResult::set_output (const char *out) +{ + output = std::string(out); +} + +std::string ScriptResult::get_output (void) +{ + return output; +} + +void ScriptResult::set_id (const char *ident) +{ + id = std::string(ident); +} + +std::string ScriptResult::get_id (void) +{ + return id; +} static int panic (lua_State *L) { @@ -77,81 +126,116 @@ return 0; } +static int checkboolean (lua_State *L, int index, const char *msg) +{ + if (!lua_isboolean(L, index)) + return luaL_error(L, "%s", msg); + else + return lua_toboolean(L, index); +} + +/* int escape_char (lua_State *L) + * + * This function is called via Lua through string.gsub. It's purpose is to + * escape characters. So the first sole character is changed to "\xxx". + */ +static int escape_char (lua_State *L) +{ + const char *a = luaL_checkstring(L, 1); + lua_pushliteral(L, "\\"); + lua_pushinteger(L, (int) *a); + lua_concat(L, 2); + return 1; +} + +/* size_t table_length (lua_State *L, int index) + * + * Returns the length of the table at index index. + * This length is the number of elements, not just array elements. + */ +static size_t table_length (lua_State *L, int index) +{ + size_t t = 0; if (index < 0) index--; + lua_pushnil(L); + while (lua_next(L, index) != 0) + { + t++; + lua_pop(L, 1); + } + return t; +} + +/* int script_updatedb (void) + * + * This procedure opens a new Lua state, initializes it, and finally + * calls init_updatedb which will look through the ./scripts directory + * for new scripts and update its database. + */ + int script_updatedb (void) { - int status; - int ret; - lua_State *L; + lua_State *L = luaL_newstate(); SCRIPT_ENGINE_VERBOSE( log_write(LOG_STDOUT, "%s: Updating rule database.\n", SCRIPT_ENGINE); ) - L = luaL_newstate(); if (L == NULL) { error("%s: Failed luaL_newstate()", SCRIPT_ENGINE); - return 0; + return SCRIPT_ENGINE_ERROR; } lua_atpanic(L, panic); - status = lua_cpcall(L, init_lua, NULL); - if (status != 0) + if (lua_cpcall(L, init_lua, NULL) != 0) { error("%s: error while initializing Lua State:\n%s\n", SCRIPT_ENGINE, lua_tostring(L, -1)); - ret = SCRIPT_ENGINE_ERROR; - goto finishup; + goto error; } lua_settop(L, 0); // safety, is 0 anyway lua_rawgeti(L, LUA_REGISTRYINDEX, errfunc); // index 1 lua_pushcclosure(L, init_updatedb, 0); - status = lua_pcall(L, 0, 0, 1); - if(status != 0) + if (lua_pcall(L, 0, 0, 1) != 0) { error("%s: error while updating Script Database:\n%s\n", SCRIPT_ENGINE, lua_tostring(L, -1)); - ret = SCRIPT_ENGINE_ERROR; - goto finishup; + goto error; } log_write(LOG_STDOUT, "NSE script database updated successfully.\n"); - finishup: - lua_close(L); - if (ret != SCRIPT_ENGINE_SUCCESS) - { - error("%s: Aborting database update.\n", SCRIPT_ENGINE); - return SCRIPT_ENGINE_ERROR; - } - else - return SCRIPT_ENGINE_SUCCESS; + lua_close(L); + return SCRIPT_ENGINE_SUCCESS; +error: + lua_close(L); + error("%s: Aborting database update.\n", SCRIPT_ENGINE); + return SCRIPT_ENGINE_ERROR; } -/* check the script-arguments provided to nmap (--script-args) before +/* int script_check_args (void) + * + * Check the script-arguments provided to nmap (--script-args) before * scanning starts - otherwise the whole scan will run through and be - * aborted before script-scanning + * aborted before script-scanning. */ int script_check_args (void) { - int ret = SCRIPT_ENGINE_SUCCESS, status; - lua_State* L = luaL_newstate(); + lua_State *L = luaL_newstate(); if (L == NULL) fatal("Error opening lua, for checking arguments\n"); lua_atpanic(L, panic); /* set all global libraries (we'll need the string-lib) */ - status = lua_cpcall(L, init_lua, NULL); - if (status != 0) + if (lua_cpcall(L, init_lua, NULL) != 0) { error("%s: error while initializing Lua State:\n%s\n", SCRIPT_ENGINE, lua_tostring(L, -1)); - ret = SCRIPT_ENGINE_ERROR; - goto finishup; + goto error; } lua_pushcclosure(L, init_parseargs, 0); @@ -159,596 +243,645 @@ lua_pcall(L, 1, 1, 0); if (!lua_isfunction(L, -1)) - ret = SCRIPT_ENGINE_ERROR; + { + error("%s: invalid --script-args\n%s\n", SCRIPT_ENGINE, + lua_tostring(L, -1)); + goto error; + } - finishup: - lua_close(L); - return ret; + lua_close(L); + return SCRIPT_ENGINE_SUCCESS; +error: + lua_close(L); + return SCRIPT_ENGINE_ERROR; } -/* open a lua instance - * open the lua standard libraries - * open all the scripts and prepare them for execution - * (export nmap bindings, add them to host/port rulesets etc.) - * apply all scripts on all hosts - * */ -int script_scan(std::vector &targets) { - int status; - std::vector::iterator target_iter; - std::list >::iterator runlevel_iter; - std::list::iterator thr_iter; - std::list torun_threads; - std::vector::iterator script_iter; - lua_State* L; - - o.current_scantype = SCRIPT_SCAN; - - SCRIPT_ENGINE_VERBOSE( - log_write(LOG_STDOUT, "%s: Initiating script scanning.\n", SCRIPT_ENGINE); - ) - - SCRIPT_ENGINE_DEBUGGING( - unsigned int tlen = targets.size(); - char targetstr[128]; - if(tlen > 1) - log_write(LOG_STDOUT, "%s: Script scanning %d hosts.\n", - SCRIPT_ENGINE, tlen); - else - log_write(LOG_STDOUT, "%s: Script scanning %s.\n", - SCRIPT_ENGINE, (*targets.begin())->NameIP(targetstr, sizeof(targetstr))); - ) - - L = luaL_newstate(); - if(L == NULL) { - error("%s: Failed luaL_newstate()", SCRIPT_ENGINE); - return SCRIPT_ENGINE_ERROR; - } - lua_atpanic(L, panic); +/* For finalizing the Target because all scripts + * have finished running against it. + */ +static int finalize_target (lua_State *L) +{ + Target *target = *((Target **) lua_touserdata(L, 1)); + target->stopTimeOutClock(NULL); + return 0; +} + +/* int loadscript (lua_State *L) + * + * This function expects on the stack: + * [1] Target * userdata + * [-1] script file closure + * This function replaces [-1] with a thread that has at the top of its stack + * an action closure with the appropriate environment. Below the action closure + * is the yield_action function which allows us to run the thread and yield + * immediately so we can use lua_gettop() on the thread when passing arguments + * back to the script as there is no need to check if the thread has been run + * yet (running the thread initially requires: lua_resume(L, lua_gettop(L) - 1) + * It also leaves on the stack (above the thread) a table with the thread's + * information: + * { + * runlevel = x.x, + * filename = "filename", + * target = Target, + * id = id || filename, + * } + */ +static lua_State *loadscript (lua_State *L) +{ + lua_State *thread; + lua_getfenv(L, -1); // get script file environment + lua_getfield(L, -1, "filename"); // get its filename + + lua_createtable(L, 0, 11); // new environment + lua_pushvalue(L, -4); // script file closure + lua_pushvalue(L, -2); // script env + lua_pushvalue(L, -4); // script filename + lua_setfield(L, -2, "filename"); + lua_pushnumber(L, 1.0); // set a default RUNLEVEL + lua_setfield(L, -2, RUNLEVEL); + lua_getfield(L, LUA_REGISTRYINDEX, "global_namespace"); // defined in init_lua + lua_pushvalue(L, -2); + lua_call(L, 1, 0); + lua_setfenv(L, -2); // new environment + + lua_call(L, 0, 0); // file closure loads globals + // On stack: script file environment (old), filename, new script file env + + thread = lua_newthread(L); + lua_getfield(L, LUA_REGISTRYINDEX, "yield_action"); + lua_getfield(L, -3, ACTION); // get action closure + lua_xmove(L, thread, 2); + + // reset old environment for script file closure (important for filename) + lua_insert(L, -5); // move new thread to script file closure location [-1] + lua_createtable(L, 0, 5); // thread info + lua_getfield(L, -2, RUNLEVEL); // get runlevel from new script file env + lua_setfield(L, -2, RUNLEVEL); // set runlevel in thread info + lua_pushvalue(L, 1); // Target userdata + lua_setfield(L, -2, TARGET); // set target + lua_pushvalue(L, -3); // filename + lua_setfield(L, -2, FILENAME); + lua_pushliteral(L, "id"); + lua_pushvalue(L, -1); + lua_gettable(L, -4); // id for script environment + if (lua_isstring(L, -1)) + lua_settable(L, -3); + else + { + lua_pushliteral(L, "filename"); + lua_replace(L, -2); + lua_gettable(L, -4); + if (lua_isstring(L, -1)) + lua_settable(L, -3); + else + luaL_error(L, "Script environment has no 'filename' or 'id' field"); + } + lua_insert(L, -5); // move thread info above new thread + lua_pop(L, 2); // filename, new script file env + lua_setfenv(L, -2); // sets old environment of script file environment + lua_pop(L, 1); // script file closure + return thread; +} + +/* int scripts_for_port (lua_State *L, Target *target, Port *port) + * + * This is an auxiliary function for preparethreads (below). + * Because we can't iterate over all ports of interest in one go we need to + * do port matching in a separate function (unlike host rule matching) + * + * Expected values for stack indices: + * [1] = Target Userdata + * [2] = nse_runlevels + * [3, -1] = PORTTESTS + */ +static void scripts_for_port (lua_State *L, Target *target, Port *port) +{ + lua_pushnil(L); + while (lua_next(L, -2) != 0) + { + // We will have a portrule function and script file closure on stack + lua_pushvalue(L, -2); // portrule function + lua_newtable(L); + set_hostinfo(L, target); + lua_newtable(L); + set_portinfo(L, port); + lua_call(L, 2, 1); // hostrule function, script closure, boolean on stack - status = lua_cpcall(L, init_lua, NULL); - if (status != 0) + if (checkboolean(L, -1, "Hostrule did not return a boolean!")) { - error("%s: error while initializing Lua State:\n%s\n", - SCRIPT_ENGINE, lua_tostring(L, -1)); - status = SCRIPT_ENGINE_ERROR; - goto finishup; + lua_State *thread; + lua_pushvalue(L, -2); // script file closure + thread = loadscript(L); + lua_newtable(L); + set_hostinfo(L, target); + lua_newtable(L); + set_portinfo(L, port); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_xmove(L, thread, 2); // two arguments for ACTION + lua_setfield(L, -3, PORT); // set in thread info table + lua_setfield(L, -2, HOST); // ... + lua_pushinteger(L, 1); // portrule type + lua_setfield(L, -2, TYPE); + + // Start thread (the yield_action function) + if (lua_resume(thread, lua_gettop(thread) - 1) != LUA_YIELD) + luaL_error(L, "%s:%d %s", __FILE__, __LINE__, + "thread did not yield immediately?"); + + lua_getfield(L, -1, RUNLEVEL); // runlevel from script info + lua_pushvalue(L, -1); // runlevel + lua_gettable(L, 2); + if (lua_isnil(L, -1)) // hasn't made runlevel table yet + { + lua_newtable(L); // runlevel table + lua_replace(L, -2); // replace nil + lua_pushvalue(L, -2); // runlevel + lua_pushvalue(L, -2); // runlevel table + lua_settable(L, 2); // set the runlevel table in nse_runlevels + } + lua_pushvalue(L, -4); // thread + lua_pushvalue(L, -4); // thread's info + lua_settable(L, -3); // set in runlevel table + SCRIPT_ENGINE_DEBUGGING( + lua_getfield(L, -3, "filename"); + log_write(LOG_STDOUT, "%s: Will run %s against %s\n", + SCRIPT_ENGINE, + lua_tostring(L, -1), + target->targetipstr()); + lua_pop(L, 1); + ) + lua_pop(L, 2); // pop: runlevel and runlevel table + lua_rawgeti(L, LUA_REGISTRYINDEX, nse_threads); // add to nse_threads + lua_insert(L, -3); + lua_settable(L, -3); + lua_pop(L, 1); // nse_threads } + lua_pop(L, 2); // pop file closure and boolean, leave key + } +} + +/* For each environment (script) in HOSTRULES and PORTRULES, + * we check if it wants to run against the target. If it does, we create + * a new thread, put the action function on its stack, put a new host table + * and port table (if applicable) to be called on the stack as well. We + * also put the thread in the nse_threads table and the runlevel table. + * + * This is to be called via cpcall() with the Target *. + */ +static int preparethreads (lua_State *L) +{ + Target *target = (Target *) lua_touserdata(L, 1); + Target **utarget; + PortList* plist = &(target->ports); + Port* port = NULL; + + lua_settop(L, 0); // clear stack + + utarget = (Target **) lua_newuserdata(L, sizeof(Target *)); // index 1 + *utarget = target; // set the target + luaL_getmetatable(L, TARGET_CLASS); + lua_setmetatable(L, -2); // set target metatable + lua_rawgeti(L, LUA_REGISTRYINDEX, nse_runlevels); // index 2 + + // Find all matching hostrules + lua_getfield(L, LUA_REGISTRYINDEX, HOSTTESTS); // index 3 + + lua_pushnil(L); + while (lua_next(L, -2) != 0) + { + // We will have a hostrule function and script file closure on stack + lua_pushvalue(L, -2); // hostrule function + lua_newtable(L); + set_hostinfo(L, target); + lua_call(L, 1, 1); // hostrule function, script closure, boolean on stack - //set the arguments - if provided - status = lua_cpcall(L, init_setargs, NULL); - if (status != 0) + if (checkboolean(L, -1, "Hostrule did not return a boolean!")) { - error("%s: error while setting arguments for scripts:\n%s\n", - SCRIPT_ENGINE, lua_tostring(L, -1)); - status = SCRIPT_ENGINE_ERROR; - goto finishup; + lua_State *thread; + lua_pushvalue(L, -2); // script file closure + thread = loadscript(L); + lua_newtable(L); + set_hostinfo(L, target); + lua_pushvalue(L, -1); + lua_xmove(L, thread, 1); // first argument for ACTION + lua_setfield(L, -2, HOST); + lua_pushinteger(L, 0); // hostrule type + lua_setfield(L, -2, TYPE); + + // Start thread (the yield_action function) + if (lua_resume(thread, lua_gettop(thread) - 1) != LUA_YIELD) + luaL_error(L, "%s:%d %s", __FILE__, __LINE__, + "thread did not yield immediately?"); + + lua_getfield(L, -1, RUNLEVEL); // runlevel from script info + lua_pushvalue(L, -1); // runlevel + lua_gettable(L, 2); + if (lua_isnil(L, -1)) // hasn't made runlevel table yet + { + lua_newtable(L); // runlevel table + lua_replace(L, -2); // replace nil + lua_pushvalue(L, -2); // runlevel + lua_pushvalue(L, -2); // runlevel table + lua_settable(L, 2); // set the runlevel table in nse_runlevels + } + lua_pushvalue(L, -4); // thread + lua_pushvalue(L, -4); // thread's info + lua_settable(L, -3); // set in runlevel table + SCRIPT_ENGINE_DEBUGGING( + lua_getfield(L, -3, "filename"); + log_write(LOG_STDOUT, "%s: Will run %s against %s\n", + SCRIPT_ENGINE, + lua_tostring(L, -1), + target->targetipstr()); + lua_pop(L, 1); + ) + lua_pop(L, 2); // pop: runlevel and runlevel table + lua_rawgeti(L, LUA_REGISTRYINDEX, nse_threads); // add to nse_threads + lua_insert(L, -3); + lua_settable(L, -3); + lua_pop(L, 1); // nse_threads + } + lua_pop(L, 2); // pop file closure and boolean, leave key + } + lua_pop(L, 1); // HOSTTESTS + + // Find all matching portrules + lua_getfield(L, LUA_REGISTRYINDEX, PORTTESTS); // index 3 + + // Because of the port iteration API we need to awkwardly iterate + // over the kinds of ports we're interested in explicitly. + while ((port = plist->nextPort(port, TCPANDUDP, PORT_OPEN)) != NULL) + scripts_for_port(L, target, port); + while((port = plist->nextPort(port, TCPANDUDP, PORT_OPENFILTERED)) != NULL) + scripts_for_port(L, target, port); + while((port = plist->nextPort(port, TCPANDUDP, PORT_UNFILTERED)) != NULL) + scripts_for_port(L, target, port); + + return 0; +} + +/* int mainloop (lua_State *L) + * + * Arguments + * Running_Scripts Table with scripts in the running state. + * + */ +static int mainloop (lua_State *L) +{ + ScanProgressMeter progress = ScanProgressMeter(SCRIPT_ENGINE); + int finished = 0; + size_t unfinished = table_length(L, 1); + double total = (double) unfinished; + + lua_settop(L, 1); lua_newtable(L); // waiting scripts + // while there are scripts in running or waiting state, we loop. + // we rely on nsock_loop to protect us from busy loops when + // all scripts are waiting. + while (!finished) + { + struct timeval now; + finished = 1; + if (l_nsock_loop(50) == NSOCK_LOOP_ERROR) + luaL_error(L, "An error occured in the nsock loop"); + + if (keyWasPressed()) + { + double done = 1.0 - (((double) unfinished) / total); + if (o.verbose > 1 || o.debugging) + { + log_write(LOG_STDOUT, "Active NSE scripts: %d\n", unfinished); + log_flush(LOG_STDOUT); + } + progress.printStats(done, NULL); } - lua_settop(L, 0); // safety, is 0 anyway - lua_rawgeti(L, LUA_REGISTRYINDEX, errfunc); // index 1 + SCRIPT_ENGINE_VERBOSE( + if (progress.mayBePrinted(NULL)) + { + double done = 1.0 - (((double) unfinished) / total); + if (o.verbose > 1 || o.debugging) + progress.printStats(done, NULL); + else + progress.printStatsIfNeccessary(done, NULL); + }) + + gettimeofday(&now, NULL); - if (!lua_checkstack(L, o.chosenScripts.size() + 1)) + lua_pushnil(L); + while (lua_next(L, 2) != 0) { - error("%s: stack overflow at %s:%d", SCRIPT_ENGINE, __FILE__, __LINE__); - status = SCRIPT_ENGINE_ERROR; - goto finishup; + Target *target; + finished = 0; // not finished + lua_getfield(L, -1, TARGET); + target = *((Target **) lua_touserdata(L, -1)); + if (target->timedOut(&now)) + { + lua_pushvalue(L, -3); // Thread + lua_pushvalue(L, -3); // Thread's info + lua_settable(L, 1); // Add to running scripts + lua_pushvalue(L, -3); // Thread + lua_pushnil(L); + lua_settable(L, 2); // Remove from waiting + } + lua_pop(L, 2); // Thread info, Target userdata (keep Thread key) } - lua_pushcclosure(L, init_rules, 0); - for (script_iter = o.chosenScripts.begin(); - script_iter != o.chosenScripts.end(); - script_iter++) - lua_pushstring(L, script_iter->c_str()); - status = lua_pcall(L, o.chosenScripts.size(), 0, 1); - if (status != 0) + + lua_pushnil(L); + while (lua_next(L, 1) != 0) { - error("%s: error while initializing script rules:\n%s\n", - SCRIPT_ENGINE, lua_tostring(L, -1)); - status = SCRIPT_ENGINE_ERROR; - goto finishup; + Target *target; + int state; + lua_State *thread = lua_tothread(L, -2); + finished = 0; // not finished + lua_getfield(L, -1, TARGET); + target = *((Target **) lua_touserdata(L, -1)); + + if (target->timedOut(&now)) + state = LUA_ERRRUN; + else + state = lua_resume(thread, lua_gettop(thread)); + switch (state) + { + case 0: + // this script has finished we first check if it produced output then + // we release the thread and remove it from the running_scripts list + if (lua_isstring(thread, 1)) + { + ScriptResult sr; + int type; + lua_getfield(L, -2, "id"); // Thread info has 'id' field + sr.set_id(lua_tostring(L, -1)); + lua_pop(L, 1); + luaL_getmetafield(thread, 1, "__index"); + lua_getfield(thread, -1, "gsub"); + lua_pushvalue(thread, 1); + lua_pushliteral(thread, "[^%w%s%p]"); + lua_pushcclosure(thread, escape_char, 0); + lua_call(thread, 3, 1); + lua_pushliteral(thread, "\""); + lua_pushvalue(thread, -1); + lua_insert(thread, -3); // surround result with quotes + lua_concat(thread, 3); + sr.set_output(lua_tostring(thread, -1)); + lua_getfield(L, -2, TYPE); + type = lua_tointeger(L, -1); + if (type == 0) + target->scriptResults.push_back(sr); + else // port + { + target->scriptResults.push_back(sr); + target->ports.numscriptresults++; + } + lua_pop(L, 1); // TYPE + } + lua_pushvalue(L, -3); // Thread + lua_pushnil(L); + lua_settable(L, 1); // Remove from running + unfinished--; + break; + case LUA_YIELD: + // this script has performed a network io operation we put it in the + // waiting when the network io operation has completed, a callback + // from the nsock library will put the script back into the running + // state + lua_pushvalue(L, -3); // Thread + lua_pushvalue(L, -3); // Thread's info + lua_settable(L, 2); // Add to waiting scripts + lua_pushvalue(L, -3); // Thread + lua_pushnil(L); + lua_settable(L, 1); // Remove from running scripts + break; + default: // error occurred + // this script returned because of an error print the failing reason + // if the verbose level is high enough, release the thread + SCRIPT_ENGINE_VERBOSE( + log_write(LOG_STDOUT, "%s: %s\n", SCRIPT_ENGINE, + lua_tostring(thread, -1)); + ) + lua_pushvalue(L, -3); // Thread + lua_pushnil(L); + lua_settable(L, 1); // Remove from running + unfinished--; + } + lua_pop(L, 2); // thread info, Target userdata } - SCRIPT_ENGINE_DEBUGGING(log_write(LOG_STDOUT, "%s: Matching rules.\n", SCRIPT_ENGINE);) + // For each script in waiting, add to running + std::list::iterator wait_i; + for (wait_i = waiting.begin(); wait_i != waiting.end(); ++wait_i) + { + lua_State *thread = (lua_State *) *wait_i; + lua_pushthread(thread); + lua_xmove(thread, L, 1); + lua_pushvalue(L, -1); + lua_gettable(L, 2); // Get thread info from waiting + if (!lua_isnil(L, -1)) + { + // It is very unlikely that a thread which is not in the waiting + // queue tries to continue; it does happen when they try to do socket + // i/o inside a pcall. This also happens when we timeout a script. + // In this case, the script is still in the waiting queue and we will + // have manually removed it from the waiting queue so we continue. + lua_pushvalue(L, -2); // Thread + lua_pushvalue(L, -2); // Thread's info + lua_settable(L, 1); // Add to running + lua_pushvalue(L, -2); // Thread + lua_pushnil(L); + lua_settable(L, 2); // Remove from waiting + } + lua_pop(L, 2); // Thread and thread info + } + waiting.clear(); + } + progress.endTask(NULL, NULL); + return 0; +} - for(target_iter = targets.begin(); target_iter != targets.end(); target_iter++) { - std::string key = ((Target*) (*target_iter))->targetipstr(); - lua_rawgeti(L, LUA_REGISTRYINDEX, current_hosts); - lua_pushstring(L, key.c_str()); - lua_pushlightuserdata(L, (void *) *target_iter); - lua_settable(L, -3); - lua_pop(L, 1); - - status = process_preparehost(L, *target_iter, torun_threads); - if(status != SCRIPT_ENGINE_SUCCESS){ - goto finishup; - } - } - - status = process_preparerunlevels(torun_threads); - if(status != SCRIPT_ENGINE_SUCCESS) { - goto finishup; - } - - SCRIPT_ENGINE_DEBUGGING(log_write(LOG_STDOUT, "%s: Running scripts.\n", SCRIPT_ENGINE);) - - for(runlevel_iter = torun_scripts.begin(); runlevel_iter != torun_scripts.end(); runlevel_iter++) { - running_scripts = (*runlevel_iter); - - SCRIPT_ENGINE_DEBUGGING(log_write(LOG_STDOUT, "%s: Runlevel: %f\n", - SCRIPT_ENGINE, - running_scripts.front().runlevel);) - - /* Start the time-out clocks for targets with scripts in this - * runlevel. The clock is stopped in process_finalize(). - */ - for (thr_iter = running_scripts.begin(); - thr_iter != running_scripts.end(); - thr_iter++) - if (!thr_iter->rr->host->timeOutClockRunning()) - thr_iter->rr->host->startTimeOutClock(NULL); - - status = process_mainloop(L); - if(status != SCRIPT_ENGINE_SUCCESS){ - goto finishup; - } - } - - -finishup: - SCRIPT_ENGINE_DEBUGGING( - log_write(LOG_STDOUT, "%s: Script scanning completed.\n", SCRIPT_ENGINE); - ) - lua_close(L); - torun_scripts.clear(); - if(status != SCRIPT_ENGINE_SUCCESS) { - error("%s: Aborting script scan.", SCRIPT_ENGINE); - return SCRIPT_ENGINE_ERROR; - } else { - return SCRIPT_ENGINE_SUCCESS; - } -} - -int process_mainloop(lua_State *L) { - int state; - int unfinished = running_scripts.size() + waiting_scripts.size(); - struct script_scan_result ssr; - struct thread_record current; - ScanProgressMeter progress = ScanProgressMeter(SCRIPT_ENGINE); - - double total = (double) unfinished; - double done = 0; - - std::list::iterator iter; - struct timeval now; - - // while there are scripts in running or waiting state, we loop. - // we rely on nsock_loop to protect us from busy loops when - // all scripts are waiting. - while( unfinished > 0 ) { - - if(l_nsock_loop(50) == NSOCK_LOOP_ERROR) { - error("%s: An error occured in the nsock loop", SCRIPT_ENGINE); - return SCRIPT_ENGINE_ERROR; - } - - unfinished = running_scripts.size() + waiting_scripts.size(); - - if (keyWasPressed()) { - done = 1.0 - (((double) unfinished) / total); - if (o.verbose > 1 || o.debugging) { - log_write(LOG_STDOUT, "Active NSE scripts: %d\n", unfinished); - log_flush(LOG_STDOUT); - } - progress.printStats(done, NULL); - } - - SCRIPT_ENGINE_VERBOSE( - if(progress.mayBePrinted(NULL)) { - done = 1.0 - (((double) unfinished) / total); - if(o.verbose > 1 || o.debugging) - progress.printStats(done, NULL); - else - progress.printStatsIfNeccessary(done, NULL); - }) - - gettimeofday(&now, NULL); - - for(iter = waiting_scripts.begin(); iter != waiting_scripts.end(); iter++) - if (iter->rr->host->timedOut(&now)) { - running_scripts.push_front((*iter)); - waiting_scripts.erase(iter); - iter = waiting_scripts.begin(); - } - - - while(running_scripts.begin() != running_scripts.end()){ - current = *(running_scripts.begin()); - - if (current.rr->host->timedOut(&now)) - state = LUA_ERRRUN; - else - state = lua_resume(current.thread, current.resume_arguments); - - if(state == LUA_YIELD) { - // this script has performed a network io operation - // we put it in the waiting - // when the network io operation has completed, - // a callback from the nsock library will put the - // script back into the running state - - waiting_scripts.push_back(current); - running_scripts.pop_front(); - } else if( state == 0) { - // this script has finished - // we first check if it produced output - // then we release the thread and remove it from the - // running_scripts list - - if(lua_isstring (current.thread, -1)) { - SCRIPT_ENGINE_TRY(process_getScriptId(current.thread, &ssr)); - ssr.output = nse_printable - (lua_tostring(current.thread, -1), lua_objlen(current.thread, -1)); - if(current.rr->type == 0) { - current.rr->host->scriptResults.push_back(ssr); - } else if(current.rr->type == 1) { - current.rr->port->scriptResults.push_back(ssr); - current.rr->host->ports.numscriptresults++; - } - lua_pop(current.thread, 2); - } - - SCRIPT_ENGINE_TRY(process_finalize(L, current.registry_idx)); - SCRIPT_ENGINE_TRY(lua_gc(L, LUA_GCCOLLECT, 0)); - } else { - // this script returned because of an error - // print the failing reason if the verbose level is high enough - SCRIPT_ENGINE_DEBUGGING( - const char* errmsg = lua_tostring(current.thread, -1); - log_write(LOG_STDOUT, "%s: %s\n", SCRIPT_ENGINE, errmsg); - ) - SCRIPT_ENGINE_TRY(process_finalize(L, current.registry_idx)); - } - } // while - } - - progress.endTask(NULL, NULL); - - return SCRIPT_ENGINE_SUCCESS; -} - -// If the target still has scripts in either running_scripts -// or waiting_scripts then it is still running. This only -// pertains to scripts in the current runlevel. - -int has_target_finished(Target *target) { - std::list::iterator iter; - - for (iter = waiting_scripts.begin(); iter != waiting_scripts.end(); iter++) - if (target == iter->rr->host) return 0; - - for (iter = running_scripts.begin(); iter != running_scripts.end(); iter++) - if (target == iter->rr->host) return 0; - - return 1; -} - -int process_finalize(lua_State* L, unsigned int registry_idx) { - luaL_unref(L, LUA_REGISTRYINDEX, registry_idx); - struct thread_record thr = running_scripts.front(); - - running_scripts.pop_front(); - - if (has_target_finished(thr.rr->host)) - thr.rr->host->stopTimeOutClock(NULL); - - return SCRIPT_ENGINE_SUCCESS; -} - -int process_waiting2running(lua_State* L, int resume_arguments) { - std::list::iterator iter; - - // find the lua state which has received i/o - for( iter = waiting_scripts.begin(); - (*iter).thread != L; - iter++) { - - // It is very unlikely that a thread which - // is not in the waiting queue tries to - // continue - // it does happen when they try to do socket i/o - // inside a pcall - - // This also happens when we timeout a script - // In this case, the script is still in the waiting - // queue and we will have manually removed it from - // the waiting queue so we just return. - - if(iter == waiting_scripts.end()) - return SCRIPT_ENGINE_SUCCESS; - } - - (*iter).resume_arguments = resume_arguments; - - // put the thread back into the running - // queue - running_scripts.push_front((*iter)); - waiting_scripts.erase(iter); - - return SCRIPT_ENGINE_SUCCESS; -} - -/* Tries to get the script id and store it in the script scan result structure - * if no 'id' field is found, the filename field is used which we set in the - * setup phase. If someone changed the filename field to a nonstring we complain +/* open a lua instance + * open the lua standard libraries + * open all the scripts and prepare them for execution + * (export nmap bindings, add them to host/port rulesets etc.) + * apply all scripts on all hosts * */ -int process_getScriptId(lua_State* L, struct script_scan_result *ssr) { +int script_scan (std::vector &targets) +{ + int i; + std::vector::iterator target_i; + std::vector::iterator script_i; + lua_State *L; - lua_getfield(L, -2, "id"); - lua_getfield(L, -3, "filename"); + o.current_scantype = SCRIPT_SCAN; - if(lua_isstring(L, -2)) { - ssr->id = strdup(lua_tostring (L, -2)); - } else if(lua_isstring(L, -1)) { - ssr->id = strdup(lua_tostring (L, -1)); - } else { - error("%s: The script has no 'id' entry, the 'filename' entry was changed to:", - SCRIPT_ENGINE); - l_dumpValue(L, -1); - return SCRIPT_ENGINE_ERROR; - } - - lua_pop(L, 2); - - return SCRIPT_ENGINE_SUCCESS; -} - -/* try all host and all port rules against the - * state of the current target - * make a list with run records for the scripts - * which want to run - * process all scripts in the list - * */ -int process_preparehost(lua_State* L, Target* target, std::list& torun_threads) { - PortList* plist = &(target->ports); - Port* current = NULL; - size_t rules_count; - unsigned int i; - std::vector torun; - std::vector::iterator iter; - struct run_record rr; - - /* find the matching hostrules - * */ - lua_getglobal(L, HOSTTESTS); - rules_count = lua_objlen(L, -1); - - for(i = 1; i <= rules_count; i++) { - lua_rawgeti(L, -1, i); - - lua_getfield(L, -1, "hostrule"); - - lua_newtable(L); - set_hostinfo(L, target); - - SCRIPT_ENGINE_LUA_TRY(lua_pcall(L, 1, 1, 0)); - - if(lua_isboolean (L, -1) && lua_toboolean(L, -1)) { - rr.type = 0; - rr.index = i; - rr.port = NULL; - rr.host = target; - torun.push_back(rr); - - SCRIPT_ENGINE_DEBUGGING( - lua_getfield(L, -2, "filename"); - log_write(LOG_STDOUT, "%s: Will run %s against %s\n", - SCRIPT_ENGINE, - lua_tostring(L, -1), - target->targetipstr()); - lua_pop(L, 1); - ) - } - lua_pop(L, 2); - } - - /* find the matching port rules - * */ - lua_getglobal(L, PORTTESTS); - - /* we only publish hostinfo once per portrule */ - lua_newtable(L); - set_hostinfo(L, target); - - /* because of the port iteration API we need to awkwardly iterate - * over the kinds of ports we're interested in explictely. - * */ - current = NULL; - while((current = plist->nextPort(current, TCPANDUDP, PORT_OPEN)) != NULL) { - SCRIPT_ENGINE_TRY(process_pickScriptsForPort(L, target, current, torun)); - } - - while((current = plist->nextPort(current, TCPANDUDP, PORT_OPENFILTERED)) != NULL) { - SCRIPT_ENGINE_TRY(process_pickScriptsForPort(L, target, current, torun)); - } - - while((current = plist->nextPort(current, TCPANDUDP, PORT_UNFILTERED)) != NULL) { - SCRIPT_ENGINE_TRY(process_pickScriptsForPort(L, target, current, torun)); - } - - // pop the hostinfo, we don't need it anymore - lua_pop(L, 1); - - /* ok, let's setup threads for the scripts which said they'd like - * to run - * Remember: - * we have the hosttestset and the porttestset on the stack! - * */ - struct thread_record tr; - - for(iter = torun.begin(); iter != torun.end(); iter++) { - /* If it is a host rule, execute the action - * and append the output to the host output i - * If it is a port rule, append the output to - * the port and increase the number of scripts - * which produced output. We need that number - * to generate beautiful output later. - * */ - switch((*iter).type) { - case 0: // this script runs against a host - lua_pushvalue(L, -2); - SCRIPT_ENGINE_TRY(process_preparethread(L, (*iter), &tr)); - lua_pop(L, 1); - break; - case 1: // this script runs against a port - lua_pushvalue(L, -1); - SCRIPT_ENGINE_TRY(process_preparethread(L, (*iter), &tr)); - lua_pop(L, 1); - break; - default: - fatal("%s: In: %s:%i This should never happen.", - SCRIPT_ENGINE, __FILE__, __LINE__); - } - - torun_threads.push_back(tr); - } - lua_pop(L, 2); - - torun.clear(); - return SCRIPT_ENGINE_SUCCESS; -} - -int process_preparerunlevels(std::list torun_threads) { - std::list current_runlevel; - std::list::iterator runlevel_iter; - double runlevel_idx = 0.0; - - torun_threads.sort(CompareRunlevels()); - - for( runlevel_iter = torun_threads.begin(); - runlevel_iter != torun_threads.end(); - runlevel_iter++) { - - if(runlevel_idx < (*runlevel_iter).runlevel) { - runlevel_idx = (*runlevel_iter).runlevel; - current_runlevel.clear(); - //push_back an empty list in which we store all scripts of the - //current runlevel... - torun_scripts.push_back(current_runlevel); - } - - torun_scripts.back().push_back(*runlevel_iter); - } - - return SCRIPT_ENGINE_SUCCESS; -} - -/* Because we can't iterate over all ports of interest in one go - * we need to do port matching in a separate function (unlike host - * rule matching) - * Note that we assume that at -2 on the stack we can find the portrules - * and at -1 the hostinfo table - * */ -int process_pickScriptsForPort( - lua_State* L, - Target* target, - Port* port, - std::vector& torun) { - size_t rules_count = lua_objlen(L, -2); - struct run_record rr; - unsigned int i; - - for(i = 1; i <= rules_count; i++) { - lua_rawgeti(L, -2, i); - - lua_getfield(L, -1, PORTRULE); - - lua_pushvalue(L, -3); - - lua_newtable(L); - set_portinfo(L, port); - - SCRIPT_ENGINE_LUA_TRY(lua_pcall(L, 2, 1, 0)); - - if(lua_isboolean (L, -1) && lua_toboolean(L, -1)) { - rr.type = 1; - rr.index = i; - rr.port = port; - rr.host = target; - torun.push_back(rr); - - SCRIPT_ENGINE_DEBUGGING( - lua_getfield(L, -2, "filename"); - log_write(LOG_STDOUT, "%s: Will run %s against %s:%d\n", - SCRIPT_ENGINE, - lua_tostring(L, -1), - target->targetipstr(), - port->portno); - lua_pop(L, 1); - ) - } else if(!lua_isboolean (L, -1)) { - lua_getfield(L, -2, "filename"); - error("%s: Rule in %s returned %s but boolean was expected.", - SCRIPT_ENGINE, - lua_tostring(L, -1), - lua_typename(L, lua_type(L, -2))); - return SCRIPT_ENGINE_LUA_ERROR; - } - lua_pop(L, 2); - } - - return SCRIPT_ENGINE_SUCCESS; -} - -/* Create a new lua thread and prepare it for execution - * we store target info in the thread so that the mainloop - * knows where to put the script result - * */ -int process_preparethread(lua_State* L, struct run_record rr, struct thread_record* tr){ + SCRIPT_ENGINE_VERBOSE( + log_write(LOG_STDOUT, "%s: Initiating script scanning.\n", SCRIPT_ENGINE); + ) - lua_State *thread = lua_newthread(L); + SCRIPT_ENGINE_DEBUGGING( + unsigned int tlen = targets.size(); + char targetstr[128]; + if(tlen > 1) + log_write(LOG_STDOUT, "%s: Script scanning %d hosts.\n", + SCRIPT_ENGINE, tlen); + else + log_write(LOG_STDOUT, "%s: Script scanning %s.\n", + SCRIPT_ENGINE, (*targets.begin())->NameIP(targetstr, sizeof(targetstr))); + ) - lua_rawgeti(L, -2, rr.index); // get the script closure + L = luaL_newstate(); + if (L == NULL) + { + error("%s: Failed luaL_newstate()", SCRIPT_ENGINE); + return SCRIPT_ENGINE_ERROR; + } + lua_atpanic(L, panic); - // move the script closure into the thread - lua_xmove(L, thread, 1); + if (lua_cpcall(L, init_lua, NULL) != 0) + { + error("%s: error while initializing Lua State:\n%s\n", + SCRIPT_ENGINE, lua_tostring(L, -1)); + goto scan_error; + } - // store the target of this thread in the thread - struct run_record *rr_thread = (struct run_record*) safe_malloc(sizeof(struct run_record)); - rr_thread->type = rr.type; - rr_thread->index = rr.index; - rr_thread->host = rr.host; - rr_thread->port = rr.port; - - - lua_getfield(thread, -1, RUNLEVEL); - tr->runlevel = lua_tonumber(thread, -1); - lua_pop(thread, 1); - - // prepare the thread for a resume by - // pushing the action method onto the stack - lua_getfield(thread, -1, ACTION); - - // make the info table - lua_newtable(thread); - set_hostinfo(thread, rr.host); - - tr->thread = thread; - tr->rr = rr_thread; - tr->resume_arguments = 1; - - // we store the thread in the registry to prevent - // garbage collection + - tr->registry_idx = luaL_ref(L, LUA_REGISTRYINDEX); - - /* if this is a host rule we don't have - * a port state - * */ - if(rr.port != NULL) { - lua_newtable(thread); - set_portinfo(thread, rr.port); - tr->resume_arguments = 2; - } + // set the arguments - if provided + if (lua_cpcall(L, init_setargs, NULL) != 0) + { + error("%s: error while setting arguments for scripts:\n%s\n", + SCRIPT_ENGINE, lua_tostring(L, -1)); + goto scan_error; + } - return SCRIPT_ENGINE_SUCCESS; -} + lua_settop(L, 0); // safety, is 0 anyway + lua_rawgeti(L, LUA_REGISTRYINDEX, errfunc); // index 1 + + if (!lua_checkstack(L, o.chosenScripts.size() + 1)) + { + error("%s: stack overflow at %s:%d", SCRIPT_ENGINE, __FILE__, __LINE__); + goto scan_error; + } + lua_pushcclosure(L, init_rules, 0); + for (script_i = o.chosenScripts.begin(); + script_i != o.chosenScripts.end(); + script_i++) + lua_pushstring(L, script_i->c_str()); + if (lua_pcall(L, o.chosenScripts.size(), 0, 1) != 0) + { + error("%s: error while initializing script rules:\n%s\n", + SCRIPT_ENGINE, lua_tostring(L, -1)); + goto scan_error; + } + + // Create Target Class + luaL_newmetatable(L, TARGET_CLASS); + lua_pushliteral(L, "__gc"); + lua_pushcclosure(L, finalize_target, 0); + lua_settable(L, -3); + lua_pop(L, 1); // metatable + + SCRIPT_ENGINE_DEBUGGING( + log_write(LOG_STDOUT, "%s: Matching rules.\n", SCRIPT_ENGINE); + ) + + lua_rawgeti(L, LUA_REGISTRYINDEX, current_hosts); + for (target_i = targets.begin(); target_i != targets.end(); target_i++) + { + lua_pushstring(L, ((Target *) (*target_i))->targetipstr()); + lua_pushlightuserdata(L, (void *) *target_i); + lua_settable(L, -3); + + // Here we call an auxiliary procedure which will get all the scripts + // that want to run against the host and put them in an array for its + // runlevel inside the nse_runlevels table. + + if (lua_cpcall(L, preparethreads, (void *) (*target_i)) != 0) + { + error("%s: error while preparing threads:\n%s\n", + SCRIPT_ENGINE, lua_tostring(L, -1)); + goto scan_error; + } + } + lua_pop(L, 1); // current_hosts + + // Sort the run levels into new auxiliary table below. The table will be an + // array with values that are the runlevels. The lowest runlevel will be at + // the largest index (stack). It should be noted there are usually only + // one runlevel so the cost of the below code is minimal. + lua_newtable(L); + lua_rawgeti(L, LUA_REGISTRYINDEX, nse_runlevels); + lua_getfield(L, LUA_REGISTRYINDEX, "tinsert"); + lua_pushnil(L); // Stack: aux table, nse_runlevels, table.insert, first key + while (lua_next(L, -3) != 0) + { + int i = lua_objlen(L, -5); // length of aux table + lua_pop(L, 1); // pop the value + lua_rawgeti(L, -4, i); // Highest value (lowest runlevel) in aux table + for (; i != 0 && !lua_lessthan(L, -2, -1); i--) + { + lua_pop(L, 1); // pop last value + lua_rawgeti(L, -4, i); + } + lua_pop(L, 1); // last value from aux table + lua_pushvalue(L, -2); // table.insert + lua_pushvalue(L, -5); // aux table + lua_pushinteger(L, i + 1); + lua_pushvalue(L, -4); // runlevel + if (lua_pcall(L, 3, 0, 1) != 0) + { + error("%s: Error while sorting runlevels:\n%s\n", + SCRIPT_ENGINE, lua_tostring(L, -1)); + goto scan_error; + } + } + lua_pop(L, 1); // table.insert + + SCRIPT_ENGINE_DEBUGGING( + log_write(LOG_STDOUT, "%s: Running scripts.\n", SCRIPT_ENGINE); + ) + + lua_pushcclosure(L, mainloop, 0); + for (i = lua_objlen(L, -3); i > 0; i--) // for each in aux table + { + lua_pushvalue(L, -1); // mainloop + lua_rawgeti(L, -4, i); // runlevel from aux table + SCRIPT_ENGINE_DEBUGGING( + log_write(LOG_STDOUT, "%s: Runlevel: %f\n", SCRIPT_ENGINE, + lua_tonumber(L, -1));) + /* Start the time-out clocks for targets with scripts in this + * runlevel. The clock is stopped in process_finalize(). + */ + lua_gettable(L, -4); // runlevel table from nse_runlevels + lua_pushnil(L); + while (lua_next(L, -2) != 0) + { + Target *target; + lua_getfield(L, -1, TARGET); + target = *((Target **) lua_touserdata(L, -1)); + if (!target->timeOutClockRunning()) + target->startTimeOutClock(NULL); + lua_pop(L, 2); // Thread info, Target userdatam + } + + if (lua_pcall(L, 1, 0, 1) != 0) + { + error("%s: Error in mainloop:\n%s\n", + SCRIPT_ENGINE, lua_tostring(L, -1)); + goto scan_error; + } + } + SCRIPT_ENGINE_DEBUGGING( + log_write(LOG_STDOUT, "%s: Script scanning completed.\n", SCRIPT_ENGINE); + ) + lua_close(L); + return SCRIPT_ENGINE_SUCCESS; +scan_error: + lua_close(L); + error("%s: Aborting script scan.", SCRIPT_ENGINE); + return SCRIPT_ENGINE_ERROR; +} +int process_waiting2running (lua_State *thread) +{ + waiting.push_front(thread); + return SCRIPT_ENGINE_SUCCESS; +} --- nmap-4.65/nse_main.h 2007-08-20 15:56:36.000000000 -0600 +++ patrick/nse_main.h 2008-06-03 01:50:20.000000000 -0600 @@ -7,18 +7,33 @@ #include #include -struct script_scan_result { - char* id; - char* output; +extern "C" { + #include "lua.h" + #include "lualib.h" + #include "lauxlib.h" +} + +class ScriptResult +{ + private: + std::string output; + std::string id; + public: + void set_output (const char *); + std::string get_output (void); + void set_id (const char *); + std::string get_id (void); }; -typedef std::vector ScriptResults; +typedef std::vector ScriptResults; class Target; int script_scan(std::vector &targets); int script_updatedb(); +int process_waiting2running(lua_State *); //parses the arguments provided to scripts via nmap's --script-args option int script_check_args(); + #endif --- nmap-4.65/Makefile.in 2008-05-30 21:24:50.000000000 -0600 +++ patrick/Makefile.in 2008-06-03 01:50:16.000000000 -0600 @@ -57,12 +57,11 @@ INSTALLNSE=@INSTALLNSE@ BUILDZENMAP=@BUILDZENMAP@ INSTALLZENMAP=@INSTALLZENMAP@ -UNINSTALLZENMAP=@UNINSTALLZENMAP@ ifneq (@LIBLUA_LIBS@,) -NSE_SRC=nse_main.cc nse_nsock.cc nse_init.cc nse_fs.cc nse_nmaplib.cc nse_debug.cc nse_pcrelib.cc nse_string.cc -NSE_HDRS=nse_main.h nse_nsock.h nse_init.h nse_fs.h nse_nmaplib.h nse_debug.h nse_macros.h nse_pcrelib.h nse_string.h -NSE_OBJS=nse_main.o nse_nsock.o nse_init.o nse_fs.o nse_nmaplib.o nse_debug.o nse_pcrelib.o nse_string.o +NSE_SRC=nse_main.cc nse_nsock.cc nse_init.cc nse_fs.cc nse_nmaplib.cc nse_debug.cc nse_pcrelib.cc +NSE_HDRS=nse_main.h nse_nsock.h nse_init.h nse_fs.h nse_nmaplib.h nse_debug.h nse_macros.h nse_pcrelib.h +NSE_OBJS=nse_main.o nse_nsock.o nse_init.o nse_fs.o nse_nmaplib.o nse_debug.o nse_pcrelib.o NSESTDLIB=nsestdlib endif @@ -106,7 +105,7 @@ cd $(NSOCKDIR)/src && $(MAKE) lua_build: $(LIBLUADIR)/Makefile - @echo Compiling liblua; cd $(LIBLUADIR) && $(MAKE) liblua.a CC="$(CC)" MYCFLAGS="$(CFLAGS) @LUA_CFLAGS@" + @echo Compiling liblua; cd $(LIBLUADIR) && $(MAKE) liblua.a @LUAFLAGS@ #$(LIBPCAPDIR)/Makefile: # @echo Configuring libpcap; cd $(LIBPCAPDIR); ./configure @@ -130,7 +129,7 @@ clean: @LUA_CLEAN@ @PCAP_CLEAN@ @PCRE_CLEAN@ @DNET_CLEAN@ nsock_clean nbase_clean my_clean @NSELIB_CLEAN@ @ZENMAP_CLEAN@ my_clean: - rm -f dependencies.mk makefile.dep + rm -f dependencies.mk rm -f $(OBJS) $(TARGET) config.cache pcap_clean: -cd $(LIBPCAPDIR) && $(MAKE) clean @@ -170,7 +169,7 @@ distclean: my_clean my_distclean @LUA_DIST_CLEAN@ @PCAP_DIST_CLEAN@ @PCRE_DIST_CLEAN@ @DNET_DIST_CLEAN@ nbase_dist_clean nsock_dist_clean @NSELIB_DIST_CLEAN@ @ZENMAP_DIST_CLEAN@ my_distclean: - rm -f Makefile Makefile.bak makefile.dep nmap_config.h stamp-h stamp-h.in \ + rm -f Makefile Makefile.bak makefile.dep config.h stamp-h stamp-h.in \ config.cache config.log config.status install-nmap: $(TARGET) @@ -179,7 +178,7 @@ # Use strip -x to avoid stripping dynamically loaded NSE functions. See # http://seclists.org/nmap-dev/2007/q4/0272.html. $(STRIP) -x $(DESTDIR)$(bindir)/nmap - $(INSTALL) -c -m 644 docs/$(TARGET).1 $(DESTDIR)$(mandir)/man1/ + $(INSTALL) -c -m 644 docs/$(TARGET).1 docs/zenmap.1 $(DESTDIR)$(mandir)/man1/ $(INSTALL) -c -m 644 docs/nmap.xsl $(DESTDIR)$(nmapdatadir)/ $(INSTALL) -c -m 644 docs/nmap.dtd $(DESTDIR)$(nmapdatadir)/ $(INSTALL) -c -m 644 nmap-services $(DESTDIR)$(nmapdatadir)/ @@ -211,9 +210,7 @@ cd $(ZENMAPDIR) && $(PYTHON) setup.py build $(if $(DESTDIR),--executable "$(DEFAULT_PYTHON_PATH)") install-zenmap: $(ZENMAPDIR)/setup.py - $(SHTOOL) mkdir -f -p -m 755 $(DESTDIR)$(bindir) $(DESTDIR)$(mandir)/man1 cd $(ZENMAPDIR) && $(PYTHON) setup.py install --prefix "$(prefix)" $(if $(DESTDIR),--root "$(DESTDIR)") - $(INSTALL) -c -m 644 docs/zenmap.1 $(DESTDIR)$(mandir)/man1/ # Create a symlink from nmapfe to zenmap if nmapfe doesn't exist or is # already a link. if [ ! -e $(DESTDIR)$(bindir)/nmapfe -o -L $(DESTDIR)$(bindir)/nmapfe ]; then \ @@ -237,21 +234,10 @@ install: install-nmap $(INSTALLNSE) $(INSTALLZENMAP) @echo "NMAP SUCCESSFULLY INSTALLED" -uninstall: uninstall-nmap $(UNINSTALLZENMAP) - -uninstall-nmap: - rm -f $(DESTDIR)$(bindir)/$(TARGET) - rm -f $(DESTDIR)$(mandir)/man1/$(TARGET).1 - rm -rf $(DESTDIR)$(nmapdatadir) $(DESTDIR)$(nmaplibexecdir) - -uninstall-zenmap: - cd $(ZENMAPDIR) && $(PYTHON) setup.py uninstall - rm -f $(DESTDIR)$(mandir)/man1/zenmap.1 -# Uninstall nmapfe only if it's a symlink. - if [ -L $(DESTDIR)$(bindir)/nmapfe ]; then \ - rm -f $(DESTDIR)$(bindir)/nmapfe; \ - fi - rm -f $(DESTDIR)$(bindir)/xnmap +uninstall: + rm -f $(bindir)/$(TARGET) $(bindir)/xnmap + rm -f $(mandir)/man1/$(TARGET).1 $(mandir)/man1/zenmap.1 + rm -rf $(nmapdatadir) $(nmaplibexecdir) ${srcdir}/configure: configure.ac cd ${srcdir} && autoconf @@ -274,5 +260,5 @@ ./config.status --recheck makefile.dep: - $(CXX) -MM $(CPPFLAGS) $(SRCS) > $@ + $(CXX) -MM $(CXXFLAGS) $(CPPFLAGS) $(SRCS) > $@ include makefile.dep --- nmap-4.65/nse_init.cc 2008-05-30 20:39:27.000000000 -0600 +++ patrick/nse_init.cc 2008-06-03 01:49:51.000000000 -0600 @@ -20,28 +20,10 @@ extern NmapOps o; extern int current_hosts; +extern int nse_threads; +extern int nse_runlevels; extern int errfunc; -/* TODO: keep? -// Error function if a user script attempts to create a new global -static int global_error(lua_State *L) -{ - lua_pushvalue(L, lua_upvalueindex(1)); - lua_pushvalue(L, 2); - if (!lua_tostring(L, -1)) - { - lua_pushliteral(L, "? (of type "); - lua_pushstring(L, lua_typename(L, lua_type(L, -2))); - lua_pushliteral(L, ")"); - lua_concat(L, 3); - lua_replace(L, -2); - } - lua_pushvalue(L, lua_upvalueindex(2)); - lua_concat(L, 3); - fprintf(stderr, "%s\n", lua_tostring(L, -1)); - return lua_error(L); -} */ - /* int error_function (lua_State *L) * * Arguments: @@ -65,15 +47,42 @@ return 1; } -/* load an nmap-lua script - * create a new closure to store the script - * tell the closure where to find the standard - * lua libs and the nmap bindings - * we do some error checking to make sure that - * the script is well formed - * the script is then added to either the hostrules - * or the portrules - * */ +static int global_namespace (lua_State *L) +{ + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, 1); + return 0; +} + +/* int yield_action (lua_State *L) + * + * This function is essentially for simplifying the call to Action + * in the main loop of nse_main. The first resume must have the function + * at the stack index [1] of the thread. All subsequent resumes are just + * lua_gettop(thread). So, we yield the thread via Lua (because we should not + * do it through C) through the code below: + */ +static int yield_action (lua_State *L) +{ + static const char yld[] = + "return function(action, ...)\n" + " yield();\n" + " return action(...);\n" + "end"; + if (luaL_loadbuffer(L, yld, sizeof(yld)-1, "yield_action") != 0) + luaL_error(L, "%s", lua_tostring(L, -1)); + lua_call(L, 0, 1); + lua_pushvalue(L, -1); // yield_action closure + lua_setfield(L, LUA_REGISTRYINDEX, "yield_action"); + lua_getglobal(L, "coroutine"); + lua_getfield(L, -1, "yield"); + lua_createtable(L, 0, 1); + lua_pushliteral(L, "yield"); + lua_pushvalue(L, -3); // coroutine.yield + lua_settable(L, -3); + lua_setfenv(L, -4); // set yld's environment + return 0; +} /* int loadfile (lua_State *L) * @@ -84,8 +93,10 @@ * The file is loaded with it's own environment that has access to the Global * Environment. The function is tested to be sure it set a global with a valid * required_fields[?] ("action", "description", ...), port or host rule. - * If it did, the script's environment (table) is saved in the global PORTTESTS - * or HOSTTESTS table. + * If it did, the script's PORT/HOST rule (function) is saved in the registry + * PORTTESTS or HOSTTESTS table with its file closure as a value. This is + * important to allow each thread to have its own action closure with its + * own locals. */ static int loadfile (lua_State *L) { @@ -97,45 +108,27 @@ lua_createtable(L, 0, 11); // Environment for script lua_pushvalue(L, 1); // tell the script about its filename - lua_setfield(L, -2, "filename"); + lua_setfield(L, -2, FILENAME); lua_pushnumber(L, 1.0); // set a default RUNLEVEL lua_setfield(L, -2, RUNLEVEL); - lua_createtable(L, 0, 1); // script gets access to global env - lua_pushvalue(L, LUA_GLOBALSINDEX); // We may want to use G(L)->mainthread - // later if this function becomes - // exposed. See lstate.h - lua_setfield(L, -2, "__index"); - lua_setmetatable(L, -2); + lua_getfield(L, LUA_REGISTRYINDEX, "global_namespace"); + lua_pushvalue(L, -2); // env + lua_call(L, 1, 0); - // TODO: Allow scripts to modify globals? - /* finally we make sure nobody tampers with the global name space any more - * and prepare for runlevel sorting - */ - /* lua_getmetatable(L, -1); - - lua_pushliteral(L, "Attempted to change the global '"); - lua_pushliteral(L, "' in "); - lua_pushstring(L, filename); - lua_pushliteral(L, " - use nmap.registry if you really want to share " - "data between scripts."); - lua_concat(L, 3); - lua_pushcclosure(L, global_error, 2); - lua_setfield(L, -2, "__newindex"); - lua_pop(L, 1); */ - - if (luaL_loadfile(L, filename) != 0) // load the file + if (luaL_loadfile(L, filename) != 0) // load the file (index 3) luaL_error(L, "'%s' could not be loaded!", filename); - lua_pushvalue(L, -2); // push environment table - lua_setfenv(L, -2); // set it + lua_pushvalue(L, -1); // file closure + lua_pushvalue(L, 2); // push environment table + lua_setfenv(L, -2); // set file environment lua_call(L, 0, 0); // Call the function (loads globals) - /* Check some required fields */ + // Check some required fields for (i = 0; i < ARRAY_LEN(required_fields); i++) { lua_pushstring(L, required_fields[i]); - lua_gettable(L, -2); + lua_gettable(L, 2); if (lua_isnil(L, -1)) luaL_error(L, "No '%s' field in script '%s'.", required_fields[i], filename); @@ -145,28 +138,36 @@ /* store the initialized test in either * the hosttests or the porttests */ - lua_getfield(L, -1, PORTRULE); // script's portrule - lua_getfield(L, -2, HOSTRULE); // script's hostrule + lua_getfield(L, -2, PORTRULE); // script's portrule + lua_getfield(L, -3, HOSTRULE); // script's hostrule /* if we are looking at a portrule then store it in the porttestsets table, * else if it is a hostrule, then it goes into the hosttestsets table, * otherwise we fail if there. */ - if (!lua_isnil(L, -2)) + if (!lua_isnil(L, -2)) // script has a port rule { - lua_pop(L, 2); // pop port/host rules - lua_getglobal(L, PORTTESTS); // Get global PORTTESTS table - lua_pushvalue(L, -2); // script's environment - lua_rawseti(L, -2, lua_objlen(L, -2) + 1); // add it - lua_pop(L, 1); // pop the porttests table + lua_getfield(L, LUA_REGISTRYINDEX, PORTTESTS); // Get PORTTESTS table + lua_pushvalue(L, -3); // script's portrule + lua_pushvalue(L, 3); // script's file closure + lua_createtable(L, 0, 1); // give script file closure new clean env + lua_pushliteral(L, "filename"); + lua_pushvalue(L, 1); // filename + lua_settable(L, -3); + lua_setfenv(L, -2); // set it + lua_settable(L, -3); } - else if (!lua_isnil(L, -1)) + else if (!lua_isnil(L, -1)) // script has a hostrule { - lua_pop(L, 2); - lua_getglobal(L, HOSTTESTS); - lua_pushvalue(L, -2); - lua_rawseti(L, -2, lua_objlen(L, -2) + 1); - lua_pop(L, 1); // pop the hosttests table + lua_getfield(L, LUA_REGISTRYINDEX, HOSTTESTS); + lua_pushvalue(L, -2); // script's hostrule + lua_pushvalue(L, 3); // script's file closure + lua_createtable(L, 0, 1); // give script file closure new clean env + lua_pushliteral(L, "filename"); + lua_pushvalue(L, 1); // filename + lua_settable(L, -3); + lua_setfenv(L, -2); // set it + lua_settable(L, -3); } else luaL_error(L, "No rules in script '%s'.", filename); @@ -180,7 +181,7 @@ * * Loads all the scripts (files with a .nse extension), using loadfile. */ -static int loaddir(lua_State *L) +static int loaddir (lua_State *L) { int i; luaL_checkstring(L, 1); // directory to load @@ -254,7 +255,7 @@ luaL_openlibs(L); // opens all standard libraries - lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); /* Loaded libraries */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); // Loaded libraries for (i = 0; i < ARRAY_LEN(libs); i++) // for each in libs { lua_pushstring(L, libs[i].name); @@ -275,10 +276,40 @@ lua_pushcclosure(L, init_setpath, 0); lua_call(L, 0, 0); + + lua_pushcclosure(L, yield_action, 0); + lua_call(L, 0, 0); lua_newtable(L); current_hosts = luaL_ref(L, LUA_REGISTRYINDEX); + lua_createtable(L, 0, 100); // threads table + lua_createtable(L, 0, 1); // threads metatable + lua_pushliteral(L, "k"); // weak keys + lua_setfield(L, -2, "__mode"); + lua_setmetatable(L, -2); + nse_threads = luaL_ref(L, LUA_REGISTRYINDEX); + + lua_createtable(L, 10, 0); // runlevels table + nse_runlevels = luaL_ref(L, LUA_REGISTRYINDEX); + + lua_createtable(L, 0, 1); // script gets access to global env + lua_pushvalue(L, LUA_GLOBALSINDEX); // We may want to use G(L)->mainthread + // later if this function becomes + // exposed. See lstate.h + lua_setfield(L, -2, "__index"); + lua_pushcclosure(L, global_namespace, 1); + lua_setfield(L, LUA_REGISTRYINDEX, "global_namespace"); + + // Add some useful Lua functions to Registry for quicker access + lua_getglobal(L, LUA_TABLIBNAME); // table + lua_getfield(L, -1, "insert"); + lua_setfield(L, LUA_REGISTRYINDEX, "tinsert"); + lua_getfield(L, -1, "remove"); + lua_setfield(L, LUA_REGISTRYINDEX, "tremove"); + lua_getfield(L, -1, "sort"); + lua_setfield(L, LUA_REGISTRYINDEX, "tsort"); + return 0; } @@ -595,7 +626,7 @@ * Arguments * ... All the categories/scripts/directories passed via --script * - * This function adds the PORTTESTS and HOSTTESTS globals to the main state. + * This function adds the PORTTESTS and HOSTTESTS to the main state. * Then it calls pick_default_categories to check for illegally passed implicit * categories (which it will add otherwise). Next, loadcategories is called * to load all the viable files for which a category was chosen. The unused @@ -607,10 +638,10 @@ int top = lua_gettop(L); // number of categories/scripts lua_newtable(L); - lua_setglobal(L, PORTTESTS); + lua_setfield(L, LUA_REGISTRYINDEX, PORTTESTS); lua_newtable(L); - lua_setglobal(L, HOSTTESTS); + lua_setfield(L, LUA_REGISTRYINDEX, HOSTTESTS); lua_pushcclosure(L, pick_default_categories, 0); lua_insert(L, 1); @@ -669,10 +700,10 @@ SCRIPT_ENGINE_DEBUGGING( int rules_count; - lua_getglobal(L, HOSTTESTS); + lua_getfield(L, LUA_REGISTRYINDEX, HOSTTESTS); rules_count = lua_objlen(L, -1); - lua_getglobal(L, PORTTESTS); + lua_getfield(L, LUA_REGISTRYINDEX, PORTTESTS); rules_count += lua_objlen(L, -1); lua_pop(L, 2); log_write(LOG_STDOUT, "%s: Initialized %d rules\n", SCRIPT_ENGINE, rules_count); --- nmap-4.65/nse_nsock.cc 2008-05-30 20:39:27.000000000 -0600 +++ patrick/nse_nsock.cc 2008-06-03 01:50:17.000000000 -0600 @@ -1,6 +1,6 @@ #include "nse_nsock.h" #include "nse_macros.h" -#include "nse_string.h" +#include "nse_main.h" #include "nse_debug.h" @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "utils.h" #include "tcpip.h" @@ -35,10 +37,6 @@ extern NmapOps o; -// defined in nse_main.cc but also declared here -// to keep the .h files clean -int process_waiting2running(lua_State *L, int resume_arguments); - static int l_nsock_connect(lua_State *L); static int l_nsock_connect_queued(lua_State *L); static int l_nsock_send(lua_State *L); @@ -70,6 +68,28 @@ const char* inet_ntop_both(int af, const void* v_addr, char* ipstring); unsigned short inet_port_both(int af, const void* v_addr); +#define ISPRINT(c) ((unsigned char)(c) > 31 && (unsigned char)(c) < 127) + +static std::string hexify (const char *str, size_t len) +{ + std::ostringstream ret; + ret << std::setbase(16) << std::setfill('0'); + + for (size_t i = 0; i < len; i += 16) + { + ret << std::setw(8) << i << ": "; + for (size_t j = i; j < i + 16; j++) + if (j < len) + ret << std::setw(2) << (unsigned int) str[j] << " "; + else + ret << " "; + for (size_t j = i; j < i + 16 && j < len; j++) + ret.put(ISPRINT(str[j]) ? (unsigned char) str[j] : ' '); + ret << std::endl; + } + return ret.str(); +} + static luaL_reg l_nsock [] = { {"connect", l_nsock_connect_queued}, {"send", l_nsock_send}, @@ -278,7 +298,7 @@ * second in l_nsock_connect). But it works for me. */ int r = l_nsock_connect(L); if(r != -1) - process_waiting2running((lua_State*) lua_state, 0); + process_waiting2running((lua_State*) lua_state); } @@ -350,17 +370,16 @@ } if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) { - process_waiting2running((lua_State*) lua_state, 1); + process_waiting2running((lua_State*) lua_state); } else { - process_waiting2running((lua_State*) lua_state, 2); + process_waiting2running((lua_State*) lua_state); } } static int l_nsock_send(lua_State *L) { l_nsock_udata* udata = (l_nsock_udata*) luaL_checkudata(L, 1, "nsock"); - const char* string = luaL_checkstring(L, 2); - size_t string_len = lua_objlen (L, 2); - char* hexified; + size_t string_len; + const char* string = luaL_checklstring(L, 2, &string_len); l_nsock_clear_buf(L,udata); @@ -371,9 +390,7 @@ } if(o.scriptTrace()) { - hexified = nse_hexify((const void*)string, string_len); - l_nsock_trace(udata->nsiod, hexified, TO); - free(hexified); + l_nsock_trace(udata->nsiod, hexify(string, string_len).c_str(), TO); } nsock_write(nsp, udata->nsiod, l_nsock_send_handler, udata->timeout, L, string, string_len); @@ -384,9 +401,9 @@ lua_State *L = (lua_State*) lua_state; if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) { - process_waiting2running((lua_State*) lua_state, 1); + process_waiting2running((lua_State*) lua_state); } else { - process_waiting2running((lua_State*) lua_state, 2); + process_waiting2running((lua_State*) lua_state); } } @@ -443,21 +460,19 @@ lua_State *L = (lua_State*) lua_state; char* rcvd_string; int rcvd_len = 0; - char* hexified; if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) { rcvd_string = nse_readbuf(nse, &rcvd_len); if(o.scriptTrace()) { - hexified = nse_hexify((const void*) rcvd_string, (size_t) rcvd_len); - l_nsock_trace(nse_iod(nse), hexified, FROM); - free(hexified); + l_nsock_trace(nse_iod(nse), + hexify(rcvd_string, (size_t) rcvd_len).c_str(), FROM); } lua_pushlstring(L, rcvd_string, rcvd_len); - process_waiting2running((lua_State*) lua_state, 2); + process_waiting2running((lua_State*) lua_state); } else { - process_waiting2running((lua_State*) lua_state, 2); + process_waiting2running((lua_State*) lua_state); } } @@ -668,7 +683,6 @@ lua_State *L = (lua_State*) lua_state; char* rcvd_string; int rcvd_len = 0; - char* hexified; int tmpidx; l_nsock_udata* udata = (l_nsock_udata*) luaL_checkudata(L, 1, "nsock"); if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) { @@ -680,9 +694,8 @@ rcvd_string = nse_readbuf(nse, &rcvd_len); if(o.scriptTrace()) { - hexified = nse_hexify((const void*) rcvd_string, (size_t) rcvd_len); - l_nsock_trace(nse_iod(nse), hexified, FROM); - free(hexified); + l_nsock_trace(nse_iod(nse), + hexify(rcvd_string, (size_t) rcvd_len).c_str(), FROM); } /* push the buffer and what we received from nsock on the stack and * concatenate both*/ @@ -697,7 +710,7 @@ */ return; } - process_waiting2running((lua_State*) lua_state, 2); + process_waiting2running((lua_State*) lua_state); } else { if(udata->bufused>1){ /*error occured after we read into some data into the buffer @@ -713,9 +726,9 @@ l_nsock_clear_buf(L, udata); udata->bufidx=tmpidx; udata->bufused=-1; - process_waiting2running((lua_State*) lua_state, 2); + process_waiting2running((lua_State*) lua_state); }else{ /*buffer should be empty */ - process_waiting2running((lua_State*) lua_state, 2); + process_waiting2running((lua_State*) lua_state); } } } @@ -1245,7 +1258,7 @@ free(nr); if(suspended) /* lua process is suspended */ - return process_waiting2running(L, 4); + return process_waiting2running(L); else /* not suspended, just pass output */ return 4; } --- nmap-4.65/nse_macros.h 2008-05-30 20:39:27.000000000 -0600 +++ patrick/nse_macros.h 2008-06-03 01:50:06.000000000 -0600 @@ -6,10 +6,17 @@ #define PORTRULE "portrule" #define PORTTESTS "porttests" #define ACTION "action" +#define HOST "host" +#define PORT "port" #define DESCRIPTION "description" #define AUTHOR "author" #define LICENSE "license" #define RUNLEVEL "runlevel" +#define TARGET_CLASS "Target Class" +#define TARGET "target" +#define TYPE "type" +#define FILENAME "filename" +#define WAITING "nse_waiting" #define FILES 1 #define DIRS 2 @@ -47,15 +54,5 @@ #define MAX_FILENAME_LEN 4096 -#define NOT_PRINTABLE '.' - -// if the character is not printable -// and the character is not a tab -// and the character is not a new line -// and the character is not a carriage return -// return 0 -// otherwise return 1 -#define ISPRINT(c) ((!(c > 31 && c < 127) && c != 9 && c != 10 && c != 13)? 0 : 1) - #endif --- nmap-4.65/nse_nmaplib.cc 2008-05-30 20:39:27.000000000 -0600 +++ patrick/nse_nmaplib.cc 2008-06-03 01:49:51.000000000 -0600 @@ -220,72 +220,77 @@ * if an os scan wasn't performed, the array * points to nil! * */ -void set_hostinfo(lua_State *L, Target *currenths) { - unsigned int i; - char hostname[1024]; - - lua_pushstring(L, strncpy(hostname, currenths->targetipstr(), 1024)); - lua_setfield(L, -2, "ip"); - - lua_pushstring(L, strncpy(hostname, currenths->HostName(), 1024)); - lua_setfield(L, -2, "name"); - - if ( currenths->TargetName() ) { // else nil - lua_pushstring(L, strncpy(hostname, currenths->TargetName(), 1024)); - lua_setfield(L, -2, "targetname"); - } - - if(currenths->directlyConnectedOrUnset() != -1){ - lua_pushboolean(L, currenths->directlyConnected()); - lua_setfield(L, -2, "directly_connected"); - } +void set_hostinfo (lua_State *L, Target *currenths) +{ + FingerPrintResults *FPR = currenths->FPR; - if(currenths->MACAddress()){ // else nil - lua_pushlstring (L, (const char*)currenths->MACAddress() , 6); - lua_setfield(L, -2, "mac_addr"); - } - if(currenths->SrcMACAddress()){ // else nil - lua_pushlstring(L, (const char*)currenths->SrcMACAddress(), 6); - lua_setfield(L, -2, "mac_addr_src"); - } - if(currenths->deviceName()){ - lua_pushstring(L, strncpy(hostname, currenths->deviceName(), 1024)); - lua_setfield(L, -2, "interface"); - } - if( (u32)(currenths->v4host().s_addr) ){ - struct in_addr adr = currenths->v4host(); - lua_pushlstring(L, (char*)&adr, 4); - lua_setfield(L, -2, "bin_ip"); - } - if( (u32)(currenths->v4source().s_addr) ){ - struct in_addr adr = currenths->v4source(); - lua_pushlstring(L, (char*)&adr, 4); - lua_setfield(L, -2, "bin_ip_src"); - } - - FingerPrintResults *FPR = NULL; + lua_pushstring(L, currenths->targetipstr()); + lua_setfield(L, -2, "ip"); - FPR = currenths->FPR; + lua_pushstring(L, currenths->HostName()); + lua_setfield(L, -2, "name"); - /* if there has been an os scan which returned a pretty certain - * result, we will use it in the scripts - * matches which aren't perfect are not needed in the scripts - */ - if( currenths->osscanPerformed() && - FPR != NULL && - FPR->overall_results == OSSCAN_SUCCESS && - FPR->num_perfect_matches > 0 && - FPR->num_perfect_matches <= 8 ) { - - lua_newtable(L); - - // this will run at least one time and at most 8 times, see if condition - for(i = 0; FPR->accuracy[i] == 1; i++) { - lua_pushstring(L, FPR->prints[i]->OS_name); - lua_rawseti(L, -2, i); - } - lua_setfield(L, -2, "os"); - } + if (currenths->TargetName()) + { + lua_pushstring(L, currenths->TargetName()); + lua_setfield(L, -2, "targetname"); // otherwise nil + } + + if (currenths->directlyConnectedOrUnset() != -1) + { + lua_pushboolean(L, currenths->directlyConnected()); + lua_setfield(L, -2, "directly_connected"); + } + + if (currenths->MACAddress()) + { + lua_pushlstring(L, (const char *) currenths->MACAddress(), 6); + lua_setfield(L, -2, "mac_addr"); + } + if (currenths->SrcMACAddress()) + { + lua_pushlstring(L, (const char *) currenths->SrcMACAddress(), 6); + lua_setfield(L, -2, "mac_addr_src"); + } + if (currenths->deviceName()) + { + lua_pushstring(L, currenths->deviceName()); + lua_setfield(L, -2, "interface"); + } + if ((u32)(currenths->v4host().s_addr)) + { + struct in_addr adr = currenths->v4host(); + lua_pushlstring(L, (const char *) &adr, 4); + lua_setfield(L, -2, "bin_ip"); + } + if ((u32)(currenths->v4source().s_addr)) + { + struct in_addr adr = currenths->v4source(); + lua_pushlstring(L, (const char *) &adr, 4); + lua_setfield(L, -2, "bin_ip_src"); + } + + /* if there has been an os scan which returned a pretty certain + * result, we will use it in the scripts + * matches which aren't perfect are not needed in the scripts + */ + if (currenths->osscanPerformed() && + FPR != NULL && + FPR->overall_results == OSSCAN_SUCCESS && + FPR->num_perfect_matches > 0 && + FPR->num_perfect_matches <= 8) + { + unsigned int i; + lua_newtable(L); + + // this will run at least one time and at most 8 times, see if condition + for (i = 0; FPR->accuracy[i] == 1; i++) + { + lua_pushstring(L, FPR->prints[i]->OS_name); + lua_rawseti(L, -2, i); + } + lua_setfield(L, -2, "os"); + } } static int l_port_accessor(lua_State *L) { @@ -520,8 +525,7 @@ return 0; } else if(lua_isboolean(L, 1) && lua_toboolean(L, 1)) { /* true. */ - lua_remove(L, 1); - return lua_gettop(L); + return lua_gettop(L) - 1; } else { fatal("%s: In: %s:%i Trying to finalize a non conforming function. Are you sure you return true on success followed by the remaining return values and nil on failure followed by an error string?", SCRIPT_ENGINE, __FILE__, __LINE__); --- nmap-4.65/output.cc 2008-05-29 14:05:02.000000000 -0600 +++ patrick/output.cc 2008-06-03 01:50:19.000000000 -0600 @@ -101,7 +101,7 @@ * * ***************************************************************************/ -/* $Id: output.cc 7764 2008-05-29 20:05:02Z david $ */ +/* $Id: output.cc 7893 2008-06-03 07:32:08Z batrick $ */ #include "output.h" #include "osscan.h" @@ -729,8 +729,9 @@ for( ssr_iter = current->scriptResults.begin(); ssr_iter != current->scriptResults.end(); ssr_iter++) { - char* xml_id= xml_convert((*ssr_iter).id); - char* xml_scriptoutput= xml_convert((*ssr_iter).output); + char* xml_id= xml_convert((*ssr_iter).get_id().c_str()); + char* xml_scriptoutput= + xml_convert((*ssr_iter).get_output().c_str()); log_write(LOG_XML, "