mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 14:11:29 +00:00
Now does most of it's work through Lua:
From Nmap-dev: "Many of the changes consist of changing how Nmap interfaces
with Lua that were sometimes awkward or inflexible. Most of the functions
have been made to be callable directly by Lua which offers many technical
advantages: stack management is alleviated, errors are handled cleanly and
are more descriptive, and there is increased reusability."
Additionally:
-- Moved all lua_State * symbols from "l" to "L". This is to maintain
consistency with other Lua libraries (convention) and to make our macros portable.
-- Moved file system manipulation over to nse_fs.cc (from nse_init.cc)
755 lines
20 KiB
C++
755 lines
20 KiB
C++
#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"
|
|
#include "portlist.h"
|
|
#include "nsock.h"
|
|
#include "NmapOps.h"
|
|
#include "timing.h"
|
|
#include "Target.h"
|
|
#include "nmap_tty.h"
|
|
|
|
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;
|
|
int errfunc = 0;
|
|
std::list<std::list<struct thread_record> > torun_scripts;
|
|
std::list<struct thread_record> running_scripts;
|
|
std::list<struct thread_record> 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<struct thread_record> torun_threads);
|
|
int process_preparehost(lua_State* L, Target* target, std::list<struct thread_record>& 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<run_record>& 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);
|
|
|
|
static int panic (lua_State *L)
|
|
{
|
|
const char *err = lua_tostring(L, 1);
|
|
fatal("Unprotected error in Lua:\n%s\n", err);
|
|
return 0;
|
|
}
|
|
|
|
int script_updatedb (void)
|
|
{
|
|
int status;
|
|
int ret;
|
|
lua_State *L;
|
|
|
|
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;
|
|
}
|
|
lua_atpanic(L, panic);
|
|
|
|
status = lua_cpcall(L, init_lua, NULL);
|
|
if (status != 0)
|
|
{
|
|
error("%s: error while initializing Lua State:\n%s\n",
|
|
SCRIPT_ENGINE, lua_tostring(L, -1));
|
|
ret = SCRIPT_ENGINE_ERROR;
|
|
goto finishup;
|
|
}
|
|
|
|
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)
|
|
{
|
|
error("%s: error while updating Script Database:\n%s\n",
|
|
SCRIPT_ENGINE, lua_tostring(L, -1));
|
|
ret = SCRIPT_ENGINE_ERROR;
|
|
goto finishup;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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
|
|
*/
|
|
int script_check_args (void)
|
|
{
|
|
int ret = SCRIPT_ENGINE_SUCCESS, status;
|
|
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)
|
|
{
|
|
error("%s: error while initializing Lua State:\n%s\n",
|
|
SCRIPT_ENGINE, lua_tostring(L, -1));
|
|
ret = SCRIPT_ENGINE_ERROR;
|
|
goto finishup;
|
|
}
|
|
|
|
lua_pushcclosure(L, init_parseargs, 0);
|
|
lua_pushstring(L, o.scriptargs);
|
|
lua_pcall(L, 1, 1, 0);
|
|
|
|
if (!lua_isfunction(L, -1))
|
|
ret = SCRIPT_ENGINE_ERROR;
|
|
|
|
finishup:
|
|
lua_close(L);
|
|
return ret;
|
|
}
|
|
|
|
/* 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<Target*> &targets) {
|
|
int status;
|
|
std::vector<Target*>::iterator target_iter;
|
|
std::list<std::list<struct thread_record> >::iterator runlevel_iter;
|
|
std::list<struct thread_record>::iterator thr_iter;
|
|
std::list<struct thread_record> torun_threads;
|
|
std::vector<std::string>::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);
|
|
|
|
status = lua_cpcall(L, init_lua, NULL);
|
|
if (status != 0)
|
|
{
|
|
error("%s: error while initializing Lua State:\n%s\n",
|
|
SCRIPT_ENGINE, lua_tostring(L, -1));
|
|
status = SCRIPT_ENGINE_ERROR;
|
|
goto finishup;
|
|
}
|
|
|
|
//set the arguments - if provided
|
|
status = lua_cpcall(L, init_setargs, NULL);
|
|
if (status != 0)
|
|
{
|
|
error("%s: error while setting arguments for scripts:\n%s\n",
|
|
SCRIPT_ENGINE, lua_tostring(L, -1));
|
|
status = SCRIPT_ENGINE_ERROR;
|
|
goto finishup;
|
|
}
|
|
|
|
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__);
|
|
status = SCRIPT_ENGINE_ERROR;
|
|
goto finishup;
|
|
}
|
|
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)
|
|
{
|
|
error("%s: error while initializing script rules:\n%s\n",
|
|
SCRIPT_ENGINE, lua_tostring(L, -1));
|
|
status = SCRIPT_ENGINE_ERROR;
|
|
goto finishup;
|
|
}
|
|
|
|
SCRIPT_ENGINE_DEBUGGING(log_write(LOG_STDOUT, "%s: Matching rules.\n", SCRIPT_ENGINE);)
|
|
|
|
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<struct thread_record>::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<struct thread_record>::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<struct thread_record>::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
|
|
* */
|
|
int process_getScriptId(lua_State* L, struct script_scan_result *ssr) {
|
|
|
|
lua_getfield(L, -2, "id");
|
|
lua_getfield(L, -3, "filename");
|
|
|
|
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<struct thread_record>& torun_threads) {
|
|
PortList* plist = &(target->ports);
|
|
Port* current = NULL;
|
|
size_t rules_count;
|
|
unsigned int i;
|
|
std::vector<run_record> torun;
|
|
std::vector<run_record>::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<struct thread_record> torun_threads) {
|
|
std::list<struct thread_record> current_runlevel;
|
|
std::list<struct thread_record>::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<run_record>& 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){
|
|
|
|
lua_State *thread = lua_newthread(L);
|
|
|
|
lua_rawgeti(L, -2, rr.index); // get the script closure
|
|
|
|
// move the script closure into the thread
|
|
lua_xmove(L, thread, 1);
|
|
|
|
// 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;
|
|
}
|
|
|
|
return SCRIPT_ENGINE_SUCCESS;
|
|
}
|
|
|
|
|