mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Merge of nse-lua (nse-lua-merge) minus most enchancements that were
not directly related to the change from C++ to Lua for the NSE main procedures. The changes are discussed in the nse-lua thread here: http://seclists.org/nmap-dev/2009/q1/0047.html
This commit is contained in:
@@ -67,9 +67,9 @@ INSTALLNDIFF=@INSTALLNDIFF@
|
||||
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_binlib.cc nse_bit.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_binlib.h nse_bit.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_binlib.o nse_bit.o
|
||||
NSE_SRC=nse_main.cc nse_nsock.cc nse_fs.cc nse_nmaplib.cc nse_debug.cc nse_pcrelib.cc nse_binlib.cc nse_bit.cc
|
||||
NSE_HDRS=nse_main.h nse_nsock.h nse_fs.h nse_nmaplib.h nse_debug.h nse_macros.h nse_pcrelib.h nse_binlib.h nse_bit.h
|
||||
NSE_OBJS=nse_main.o nse_nsock.o nse_fs.o nse_nmaplib.o nse_debug.o nse_pcrelib.o nse_binlib.o nse_bit.o
|
||||
ifneq (@OPENSSL_LIBS@,)
|
||||
NSE_SRC+=nse_openssl.cc
|
||||
NSE_HDRS+=nse_openssl.h
|
||||
@@ -247,6 +247,7 @@ NSE_FILES = scripts/script.db scripts/*.nse
|
||||
NSE_LIB_LUA_FILES = nselib/*.lua
|
||||
|
||||
install-nse: $(TARGET)
|
||||
$(INSTALL) -c -m 644 nse_main.lua $(DESTDIR)$(nmapdatadir)/
|
||||
$(INSTALL) -d $(DESTDIR)$(nmapdatadir)/scripts
|
||||
cp -f $(NSE_FILES) $(DESTDIR)$(nmapdatadir)/scripts
|
||||
$(INSTALL) -d $(DESTDIR)$(nmapdatadir)/nselib
|
||||
|
||||
10
nmap.cc
10
nmap.cc
@@ -698,8 +698,6 @@ int nmap_main(int argc, char *argv[]) {
|
||||
o.chooseScripts(optarg);
|
||||
} else if(optcmp(long_options[option_index].name,"script-args")==0){
|
||||
o.scriptargs=strdup(optarg);
|
||||
if(script_check_args()!=0)
|
||||
fatal("Error parsing --script-args\n");
|
||||
}else if (optcmp(long_options[option_index].name, "script-trace") == 0) {
|
||||
o.scripttrace = 1;
|
||||
} else if (optcmp(long_options[option_index].name, "script-updatedb") == 0){
|
||||
@@ -1581,6 +1579,9 @@ int nmap_main(int argc, char *argv[]) {
|
||||
// disable warnings
|
||||
o.max_ips_to_scan = o.numhosts_scanned;
|
||||
}
|
||||
if (o.servicescan)
|
||||
o.scriptversion = 1;
|
||||
open_nse();
|
||||
#endif
|
||||
|
||||
/* Time to create a hostgroup state object filled with all the requested
|
||||
@@ -1782,9 +1783,6 @@ int nmap_main(int argc, char *argv[]) {
|
||||
|
||||
if (o.servicescan) {
|
||||
o.current_scantype = SERVICE_SCAN;
|
||||
#ifndef NOLUA
|
||||
o.scriptversion = 1;
|
||||
#endif
|
||||
|
||||
service_scan(Targets);
|
||||
}
|
||||
@@ -1907,7 +1905,7 @@ void nmap_free_mem() {
|
||||
if (o.extra_payload) free(o.extra_payload);
|
||||
if (o.ipoptions) free(o.ipoptions);
|
||||
#ifndef NOLUA
|
||||
script_scan_free();
|
||||
close_nse();
|
||||
free(o.scriptargs);
|
||||
#endif
|
||||
}
|
||||
|
||||
797
nse_init.cc
797
nse_init.cc
@@ -1,797 +0,0 @@
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h" /* for libraries */
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
|
||||
#include "nse_init.h"
|
||||
#include "nse_nmaplib.h"
|
||||
#include "nse_macros.h"
|
||||
#include "nse_debug.h"
|
||||
#include "nse_fs.h"
|
||||
|
||||
// 3rd Party libs
|
||||
#include "nse_pcrelib.h"
|
||||
#include "nse_bit.h"
|
||||
|
||||
#include "nse_binlib.h"
|
||||
|
||||
#include "nbase.h"
|
||||
|
||||
#include "nmap.h"
|
||||
#include "nmap_error.h"
|
||||
#include "NmapOps.h"
|
||||
|
||||
#include "errno.h"
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include "nse_openssl.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
extern NmapOps o;
|
||||
|
||||
extern int current_hosts;
|
||||
extern int errfunc;
|
||||
|
||||
/* int error_function (lua_State *L)
|
||||
*
|
||||
* Arguments:
|
||||
* -- error_message (passed by Lua)
|
||||
*
|
||||
* This function is for use with lua_pcall as the error handler.
|
||||
* Because the stack is not unwound when this is called,
|
||||
* we are able to obtain a traceback of the current stack frame.
|
||||
* We use debug.traceback (an upvalue set in init_lua) for the real work.
|
||||
*/
|
||||
static int error_function (lua_State *L) // for use with lua_pcall
|
||||
{
|
||||
luaL_where(L, 1);
|
||||
lua_insert(L, 1);
|
||||
lua_pushvalue(L, lua_upvalueindex(1)); // debug.traceback
|
||||
lua_pushthread(L);
|
||||
lua_pushliteral(L, "");
|
||||
lua_pushinteger(L, 2);
|
||||
lua_call(L, 3, 1);
|
||||
lua_concat(L, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* int loadfile (lua_State *L)
|
||||
*
|
||||
* Arguments
|
||||
* -- filename File to load
|
||||
*
|
||||
* This function loads a file as a new script, unless it has already been
|
||||
* loaded.
|
||||
*
|
||||
* 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 is added to the SCRIPTFILES table and 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)
|
||||
{
|
||||
int i;
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
static const char *required_fields[] = {ACTION, DESCRIPTION};
|
||||
|
||||
lua_settop(L, 1); // removes other arguments
|
||||
|
||||
/* Is this file already loaded? */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, SCRIPTFILES);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_gettable(L, -2);
|
||||
if (lua_toboolean(L, -1))
|
||||
return 0;
|
||||
lua_pop(L, 2);
|
||||
|
||||
lua_createtable(L, 0, 11); // Environment for script (index 2)
|
||||
|
||||
lua_pushvalue(L, 1); // tell the script about its 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);
|
||||
|
||||
if (luaL_loadfile(L, filename) != 0) // load the file (index 3)
|
||||
{
|
||||
error("%s: '%s' could not be compiled.", SCRIPT_ENGINE, filename);
|
||||
SCRIPT_ENGINE_DEBUGGING(
|
||||
error("%s", lua_tostring(L, -1));
|
||||
)
|
||||
return 0;
|
||||
}
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushvalue(L, 2); // push environment table
|
||||
lua_setfenv(L, -2); // set it
|
||||
if (lua_pcall(L, 0, 0, 0) != 0) // Call the function (loads globals)
|
||||
{
|
||||
error("%s: '%s' threw a run time error and could not be loaded.",
|
||||
SCRIPT_ENGINE, filename);
|
||||
SCRIPT_ENGINE_DEBUGGING(
|
||||
error("%s", lua_tostring(L, -1));
|
||||
)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check some required fields
|
||||
for (i = 0; i < ARRAY_LEN(required_fields); i++)
|
||||
{
|
||||
lua_pushstring(L, required_fields[i]);
|
||||
lua_gettable(L, 2);
|
||||
if (lua_isnil(L, -1))
|
||||
{
|
||||
error("%s: '%s' does not have required field '%s'", SCRIPT_ENGINE, filename,
|
||||
required_fields[i]);
|
||||
return 0;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
/* store the initialized test in either
|
||||
* the hosttests or the porttests
|
||||
*/
|
||||
lua_getfield(L, 2, PORTRULE); // script's portrule
|
||||
lua_getfield(L, 2, 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)) // script has a port rule
|
||||
{
|
||||
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_getfenv(L, -1);
|
||||
lua_pushliteral(L, FILENAME);
|
||||
lua_pushvalue(L, 1); // filename
|
||||
lua_settable(L, -3);
|
||||
lua_pop(L, 1); // file closure environment
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
else if (!lua_isnil(L, -1)) // script has a hostrule
|
||||
{
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, HOSTTESTS);
|
||||
lua_pushvalue(L, -2); // script's hostrule
|
||||
lua_pushvalue(L, 3); // script's file closure
|
||||
lua_getfenv(L, -1);
|
||||
lua_pushliteral(L, FILENAME);
|
||||
lua_pushvalue(L, 1); // filename
|
||||
lua_settable(L, -3);
|
||||
lua_pop(L, 1); // file closure environment
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
else
|
||||
error("%s: '%s' does not have a portrule or hostrule.", SCRIPT_ENGINE,
|
||||
filename);
|
||||
|
||||
/* Record the file as loaded. */
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, SCRIPTFILES);
|
||||
lua_pushstring(L, filename);
|
||||
lua_pushboolean(L, true);
|
||||
lua_settable(L, -3);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* int loaddir (lua_State *L)
|
||||
*
|
||||
* Arguments
|
||||
* -- directory Directory (string) to load.
|
||||
*
|
||||
* Loads all the scripts (files with a .nse extension), using loadfile.
|
||||
*/
|
||||
static int loaddir (lua_State *L)
|
||||
{
|
||||
int i;
|
||||
luaL_checkstring(L, 1); // directory to load
|
||||
|
||||
lua_pushcclosure(L, nse_scandir, 0);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushinteger(L, FILES);
|
||||
lua_call(L, 2, 1);
|
||||
|
||||
lua_pushcclosure(L, loadfile, 0);
|
||||
for (i = 1; i <= (int) lua_objlen(L, -2); i++)
|
||||
{
|
||||
lua_pushvalue(L, -1); // loadfile closure
|
||||
lua_rawgeti(L, -3, i); // filename
|
||||
lua_call(L, 1, 0); // load it
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* int init_setpath (lua_State *L)
|
||||
*
|
||||
* Sets the search path of require function to include:
|
||||
* ./nselib/ For Lua Path (.lua files)
|
||||
*/
|
||||
static int init_setpath (lua_State *L)
|
||||
{
|
||||
char path[MAX_FILENAME_LEN];
|
||||
|
||||
/* set the path lua searches for modules*/
|
||||
if (nmap_fetchfile(path, MAX_FILENAME_LEN, SCRIPT_ENGINE_LIB_DIR) != 2)
|
||||
luaL_error(L, "'%s' not a directory", SCRIPT_ENGINE_LIB_DIR);
|
||||
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
|
||||
lua_getfield(L, -1, LUA_LOADLIBNAME); /* "package" */
|
||||
|
||||
lua_pushstring(L, path);
|
||||
lua_pushliteral(L, "?.lua;");
|
||||
lua_getfield(L, -3, "path"); /* package.path */
|
||||
lua_concat(L, 3);
|
||||
lua_setfield(L, -2, "path");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* int init_lua (lua_State *L)
|
||||
*
|
||||
* Initializes the Lua State.
|
||||
* Opens standard libraries as well as nmap and pcre.
|
||||
* Sets an error function for use by pcall.
|
||||
* Sets the path for require.
|
||||
*/
|
||||
int init_lua (lua_State *L)
|
||||
{
|
||||
int i;
|
||||
static const luaL_Reg libs[] = {
|
||||
{NSE_PCRELIBNAME, luaopen_pcrelib}, // pcre library
|
||||
{"nmap", luaopen_nmap}, // nmap bindings
|
||||
{NSE_BINLIBNAME, luaopen_binlib},
|
||||
{BITLIBNAME, luaopen_bit}, // bit library
|
||||
#ifdef HAVE_OPENSSL
|
||||
{OPENSSLLIBNAME, luaopen_openssl}, // openssl bindings
|
||||
#endif
|
||||
{"stdnse.c", luaopen_stdnse_c},
|
||||
};
|
||||
|
||||
luaL_openlibs(L); // opens all standard 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);
|
||||
lua_pushcclosure(L, libs[i].func, 0);
|
||||
lua_pushvalue(L, -2);
|
||||
lua_call(L, 1, 1);
|
||||
if (lua_isnil(L, -1))
|
||||
{
|
||||
lua_getglobal(L, libs[i].name); // library?
|
||||
if (!lua_istable(L, -1))
|
||||
{
|
||||
lua_pop(L, 2);
|
||||
lua_pushboolean(L, true);
|
||||
}
|
||||
else
|
||||
lua_replace(L, -2);
|
||||
}
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
lua_pop(L, 1); // _LOADED
|
||||
|
||||
lua_getglobal(L, "debug"); // debug
|
||||
lua_getfield(L, -1, "traceback"); lua_replace(L, -2); // replace debug table
|
||||
lua_pushcclosure(L, error_function, 1);
|
||||
errfunc = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
|
||||
lua_pushcclosure(L, init_setpath, 0);
|
||||
lua_call(L, 0, 0);
|
||||
|
||||
lua_newtable(L);
|
||||
current_hosts = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* int init_parseargs (lua_State *L)
|
||||
*
|
||||
* Arguments
|
||||
* args Arguments passed through --script-args
|
||||
* Returns
|
||||
* function Function that returns a table with the arguments, or an error
|
||||
* message describing why the arguments could not be parsed.
|
||||
*/
|
||||
int init_parseargs (lua_State *L)
|
||||
{
|
||||
const char *arg;
|
||||
size_t len;
|
||||
|
||||
luaL_checkstring(L, 1);
|
||||
lua_getfield(L, 1, "gsub"); // string.gsub
|
||||
lua_pushvalue(L, 1);
|
||||
lua_pushliteral(L, "=([^{},]+)"); // make strings quoted
|
||||
lua_pushliteral(L, "=\"%1\"");
|
||||
lua_call(L, 3, 1);
|
||||
|
||||
lua_pushliteral(L, "return {");
|
||||
lua_insert(L, -2);
|
||||
lua_pushliteral(L, "}");
|
||||
lua_concat(L, 3);
|
||||
arg = lua_tolstring(L, -1, &len);
|
||||
luaL_loadbuffer(L, arg, len, "Script-Args");
|
||||
|
||||
return 1; // return function from luaL_loadbuffer or error message returned
|
||||
}
|
||||
|
||||
/* int init_setargs (lua_State *L)
|
||||
*
|
||||
* Takes the function returned by init_parseargs(), calls it, and puts
|
||||
* the returned table in nmap.registry.args
|
||||
*/
|
||||
int init_setargs (lua_State *L)
|
||||
{
|
||||
lua_getglobal(L, "nmap");
|
||||
lua_getfield(L, -1, "registry");
|
||||
|
||||
lua_pushcclosure(L, init_parseargs, 0);
|
||||
lua_pushstring(L, o.scriptargs);
|
||||
lua_call(L, 1, 1);
|
||||
|
||||
if (!lua_isfunction(L, -1))
|
||||
luaL_error(L, "Bad script arguments!\n\t%s", lua_tostring(L, -1));
|
||||
|
||||
lua_call(L, 0, 1); /* get returned table */
|
||||
|
||||
lua_setfield(L, -2, "args");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Sorts the table at the given stack index (by calling table.sort). */
|
||||
static void table_sort(lua_State *L, int index)
|
||||
{
|
||||
/* table.sort sorts in place. We modify the original by calling the function
|
||||
on a copied reference to the table */
|
||||
lua_pushvalue(L, index);
|
||||
/* Get table.sort. */
|
||||
lua_getglobal(L, "table");
|
||||
lua_getfield(L, -1, "sort");
|
||||
lua_replace(L, -2);
|
||||
/* Put the (copy of the) table after the function. */
|
||||
lua_insert(L, -2);
|
||||
lua_call(L, 1, 0);
|
||||
}
|
||||
|
||||
/* int init_updatedb (lua_State *L)
|
||||
*
|
||||
* Loads all the files in ./scripts and puts them in the database.
|
||||
* Each file is loaded and for each of its categories, an entry in the
|
||||
* database is made in the following format:
|
||||
* Entry{ category = "category1", filename = "somefile" }\n"
|
||||
* Entry{ category = "category2", filename = "somefile" }\n"
|
||||
* Each file will have an entry per category.
|
||||
*/
|
||||
int init_updatedb (lua_State *L)
|
||||
{
|
||||
int i;
|
||||
char path[MAX_FILENAME_LEN];
|
||||
FILE *scriptdb;
|
||||
lua_settop(L, 0); // clear all args
|
||||
|
||||
if (nmap_fetchfile(path, sizeof(path) - sizeof(SCRIPT_ENGINE_DATABASE),
|
||||
SCRIPT_ENGINE_LUA_DIR) == 0)
|
||||
luaL_error(L, "Couldn't find '%s'", SCRIPT_ENGINE_LUA_DIR);
|
||||
|
||||
lua_pushcclosure(L, nse_scandir, 0);
|
||||
lua_pushstring(L, path);
|
||||
lua_pushinteger(L, FILES);
|
||||
lua_call(L, 2, 1); // get all the .nse files in ./scripts
|
||||
|
||||
/* Sort what we get from nse_scandir so that script.db diffs are useful. */
|
||||
table_sort(L, 1);
|
||||
|
||||
// we rely on the fact that nmap_fetchfile returned a string which leaves enough room
|
||||
// to append the db filename (see call to nmap_fetchfile above)
|
||||
strncat(path, SCRIPT_ENGINE_DATABASE, MAX_FILENAME_LEN-1);
|
||||
|
||||
scriptdb = fopen(path, "w");
|
||||
if (scriptdb == NULL)
|
||||
luaL_error(L, "Could not open file '%s' for writing.", path);
|
||||
|
||||
SCRIPT_ENGINE_DEBUGGING(
|
||||
log_write(LOG_STDOUT, "%s: Trying to add %u scripts to the database.\n",
|
||||
SCRIPT_ENGINE, lua_objlen(L, 1));
|
||||
)
|
||||
|
||||
// give the script global namespace access
|
||||
lua_createtable(L, 0, 1); // metatable
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
for (i = 1; i <= (int) lua_objlen(L, 1); i++)
|
||||
{
|
||||
const char *file;
|
||||
lua_rawgeti(L, 1, i); // integer key from scan_dir() table
|
||||
file = lua_tostring(L, -1);
|
||||
if (nse_check_extension(SCRIPT_ENGINE_EXTENSION, file) &&
|
||||
strstr(file, SCRIPT_ENGINE_DATABASE) == NULL)
|
||||
{
|
||||
char *filebase = path_get_basename(file);
|
||||
lua_newtable(L); // script environment
|
||||
lua_pushvalue(L, -3); // script metatable
|
||||
lua_setmetatable(L, -2); // set it
|
||||
if (luaL_loadfile(L, file) != 0) // load file
|
||||
luaL_error(L, "file '%s' could not be loaded", file);
|
||||
lua_pushvalue(L, -2); // push environment
|
||||
lua_setfenv(L, -2); // set it
|
||||
if ( lua_pcall(L, 0, 0, 0) != 0 ) {
|
||||
// skip scripts that produce errors
|
||||
log_write(LOG_STDOUT, "%s: Skipping script '%s' because it produced errors while loading.\n",
|
||||
SCRIPT_ENGINE, file );
|
||||
SCRIPT_ENGINE_VERBOSE(
|
||||
error("%s", lua_tostring(L, -1));
|
||||
)
|
||||
lua_pop(L, 3);
|
||||
continue;
|
||||
}
|
||||
|
||||
lua_getfield(L, -1, "categories");
|
||||
if (lua_isnil(L, -1))
|
||||
luaL_error(L, "Script, '%s', being added to the database "
|
||||
"has no categories.", file);
|
||||
|
||||
if (filebase == NULL)
|
||||
luaL_error(L, "filename basename could not be generated");
|
||||
|
||||
lua_getglobal(L, "string");
|
||||
lua_getfield(L, -1, "lower"); lua_replace(L, -2);
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -3) != 0)
|
||||
{
|
||||
lua_pushvalue(L, -3); // string.lower
|
||||
lua_insert(L, -2); // put below category string
|
||||
lua_call(L, 1, 1); // lowered string on stack
|
||||
fprintf(scriptdb, "Entry{ category = \"%s\", filename = \"%s\" }\n",
|
||||
lua_tostring(L, -1), filebase);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 3); // script environment, categories, string.lower
|
||||
free(filebase);
|
||||
}
|
||||
lua_pop(L, 1); // filename
|
||||
}
|
||||
|
||||
if (fclose(scriptdb) != 0)
|
||||
luaL_error(L, "Could not close script.db: %s.", strerror(errno));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct extensional_category {
|
||||
const char *category;
|
||||
int option;
|
||||
} extensional_category;
|
||||
|
||||
/* int pick_default_categories (lua_State *L)
|
||||
*
|
||||
* This function takes as arguments all the scripts/categories/directories
|
||||
* passed to the --script command line option, and augments them with any other
|
||||
* categories that should be added. These are "default" if script scanning was
|
||||
* requested and no scripts were given on the command line, and "version" if
|
||||
* version scanning was requested.
|
||||
*
|
||||
* If a "reserved" category (currently only "version") was listed on the command
|
||||
* line, give a fatal error.
|
||||
*/
|
||||
static int pick_default_categories (lua_State *L)
|
||||
{
|
||||
int i, top = lua_gettop(L);
|
||||
extensional_category reserved_categories[] = {
|
||||
{"version", o.scriptversion},
|
||||
};
|
||||
|
||||
if (top > 0)
|
||||
{
|
||||
// if they tried to explicitely select an implicit category, we complain
|
||||
// ... for each in reserved_categories
|
||||
for (i = 0; i < ARRAY_LEN(reserved_categories); i++)
|
||||
{
|
||||
int j;
|
||||
lua_pushstring(L, reserved_categories[i].category);
|
||||
for (j = 1; j <= top; j++)
|
||||
{
|
||||
lua_getglobal(L, "string");
|
||||
lua_getfield(L, -1, "lower"); lua_replace(L, -2);
|
||||
lua_pushvalue(L, j);
|
||||
lua_call(L, 1, 1);
|
||||
if (lua_equal(L, -1, -2))
|
||||
{
|
||||
fatal("%s: specifying the \"%s\" category explicitly is not allowed.",
|
||||
SCRIPT_ENGINE, lua_tostring(L, -1));
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
else if (o.script == 1)
|
||||
lua_pushliteral(L, "default"); // default set of categories
|
||||
|
||||
// for each in reserved_categories
|
||||
for (i = 0; i < ARRAY_LEN(reserved_categories); i++)
|
||||
if (reserved_categories[i].option == 1)
|
||||
lua_pushstring(L, reserved_categories[i].category);
|
||||
|
||||
return lua_gettop(L);
|
||||
}
|
||||
|
||||
/* int entry (lua_State *L)
|
||||
*
|
||||
* This function is called for each line of script.db, and is responsible for
|
||||
* loading the scripts that are in requested categories.
|
||||
*
|
||||
* script.db is executable Lua code that makes calls to this function, with
|
||||
* lines like
|
||||
*
|
||||
* Entry{ category = "default", filename = "script.nse" }
|
||||
*
|
||||
* The function has one upvalue, which is used to accumulate results while the
|
||||
* database is executed. It is a table of the categories/scripts/directories
|
||||
* requested, all initially mapping to false, plus canonicalization mappings
|
||||
* (see loadcategories).
|
||||
*
|
||||
* This function receives a table with a category and a filename. A filename is
|
||||
* loaded if
|
||||
* 1. its category is in the list of requested categories/scripts/directories,
|
||||
* or
|
||||
* 2. the category "all" was requested and the category of the script is not
|
||||
* "version".
|
||||
*/
|
||||
static int entry (lua_State *L)
|
||||
{
|
||||
char script_path[MAX_FILENAME_LEN];
|
||||
int not_all;
|
||||
|
||||
luaL_checktype(L, 1, LUA_TTABLE); // Sole argument is a table
|
||||
lua_settop(L, 1);
|
||||
lua_getfield(L, 1, CATEGORY); // index 2
|
||||
lua_getfield(L, 1, FILENAME); // index 3
|
||||
if (!(lua_isstring(L, 2) && lua_isstring(L, 3)))
|
||||
luaL_error(L, "bad entry in script database");
|
||||
lua_pushvalue(L, 3); // filename
|
||||
|
||||
/* Push values that are used to decide whether to load this file. */
|
||||
lua_pushvalue(L, 2); // Category name.
|
||||
lua_gettable(L, lua_upvalueindex(1)); // If non-nil: a requested category.
|
||||
lua_pushliteral(L, "version"); // For literal comparison against the "version" category.
|
||||
lua_getfield(L, lua_upvalueindex(1), "all"); // If non-nil: "all" was requested.
|
||||
|
||||
// If category chosen OR ("all" chosen AND category != "version")
|
||||
if ((not_all = (!lua_isnil(L, -3))) ||
|
||||
(!(lua_isnil(L, -1) || lua_equal(L, 2, -2))))
|
||||
{
|
||||
/* Mark this category as used. */
|
||||
if (not_all)
|
||||
lua_pushvalue(L, 2);
|
||||
else
|
||||
lua_pushliteral(L, "all");
|
||||
lua_pushvalue(L, -1);
|
||||
lua_gettable(L, lua_upvalueindex(1));
|
||||
|
||||
/* Is this a canonicalization entry pointing to the real key? (See
|
||||
* loadcategories.) */
|
||||
if (!lua_isboolean(L, -1)) // points to real key?
|
||||
{
|
||||
/* If yes, point the real key to true. */
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushboolean(L, true);
|
||||
lua_settable(L, lua_upvalueindex(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If no, just point the category name to true. */
|
||||
lua_pushvalue(L, -2);
|
||||
lua_pushboolean(L, true);
|
||||
lua_settable(L, lua_upvalueindex(1));
|
||||
}
|
||||
lua_pop(L, 1); // Pop Boolean.
|
||||
|
||||
/* Load the file and insert its name into the second upvalue, the table of
|
||||
* loaded filenames. The value is true. */
|
||||
if (nse_fetchfile(script_path, sizeof(script_path),
|
||||
lua_tostring(L, 3)) != 1)
|
||||
luaL_error(L, "%s is not a file!", lua_tostring(L, 3));
|
||||
|
||||
/* Finally, load the file (load its portrule or hostrule). */
|
||||
lua_pushcclosure(L, loadfile, 0);
|
||||
lua_pushstring(L, script_path);
|
||||
lua_call(L, 1, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* int loadcategories (lua_State *L)
|
||||
*
|
||||
* This function takes all the categories/scripts/directories passed to it,
|
||||
* loads the script files belonging to any of the arguments that are categories,
|
||||
* and returns what's left over (script filenames, directory names, or possibly
|
||||
* unused category names) in a table. The unused names all map to false. */
|
||||
static int loadcategories (lua_State *L)
|
||||
{
|
||||
int i, top = lua_gettop(L);
|
||||
char c_dbpath[MAX_FILENAME_LEN];
|
||||
static const char *dbpath = SCRIPT_ENGINE_LUA_DIR SCRIPT_ENGINE_DATABASE;
|
||||
|
||||
/* Build the script database if it doesn't exist. */
|
||||
if (nmap_fetchfile(c_dbpath, sizeof(c_dbpath), dbpath) == 0)
|
||||
{
|
||||
lua_pushcclosure(L, init_updatedb, 0);
|
||||
lua_call(L, 0, 0);
|
||||
}
|
||||
|
||||
/* Create a table that is used to keep track of which categories/scripts/
|
||||
* directories are used and unused. (Because this function deals only with
|
||||
* categories, script filenames and directory names always come out unused.)
|
||||
* We build a table with every script/category/directory mapped to false.
|
||||
* Additionally we map a lower-case version of every string to the original
|
||||
* string (this is to canonicalize category names). Logic in the entry
|
||||
* function checks for this canonicalization step.
|
||||
*
|
||||
* The entry function adjusts the values in the table to true as files are
|
||||
* loaded. Later, all the keys that map to true are removed, leaving only the
|
||||
* unused scripts/categories/directories. Because all strings are considered
|
||||
* true, the canonicalization entries will be considered "used" and
|
||||
* removed as well. */
|
||||
lua_createtable(L, 0, top); // categories table
|
||||
for (i = 1; i <= top; i++)
|
||||
{
|
||||
/* Create the canonicalization entry mapping the lower-case string to the
|
||||
* original string. Do this first in case the string maps to itself (i.e.,
|
||||
* it was lower-case to begin with). In that case the mapping to false will
|
||||
* replace this mapping, and no canonicalization is needed. */
|
||||
lua_getglobal(L, "string");
|
||||
lua_getfield(L, -1, "lower"); lua_replace(L, -2);
|
||||
lua_pushvalue(L, i); // Category/script/directory.
|
||||
lua_call(L, 1, 1); // Canonicalize it.
|
||||
lua_pushvalue(L, i);
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* Now map the name to false, meaning we assume the category/script/
|
||||
* directory is unused until the entry function marks it as used. */
|
||||
lua_pushvalue(L, i); // Category/script/directory.
|
||||
lua_pushboolean(L, false);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
|
||||
/* Execute script.db with the Entry closure as the only thing in its
|
||||
* environment (see the entry function). Entry has an upvalue: the used/unused
|
||||
* table just created. Entry will mark categories/scripts/directories as used
|
||||
* in the table as files are loaded. */
|
||||
luaL_loadfile(L, c_dbpath);
|
||||
lua_createtable(L, 0, 1);
|
||||
lua_pushliteral(L, "Entry");
|
||||
lua_pushvalue(L, -4); // Used/unused table.
|
||||
lua_pushcclosure(L, entry, 1);
|
||||
lua_settable(L, -3);
|
||||
lua_setfenv(L, -2); // Put the Entry function in the global environment.
|
||||
lua_call(L, 0, 0); // Execute the script database, letting errors go through.
|
||||
|
||||
/* Go through and remove all the used categories, leaving only the unused
|
||||
* categories/scripts/directories. */
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0)
|
||||
{
|
||||
if (lua_toboolean(L, -1)) // If used
|
||||
{
|
||||
lua_pushvalue(L, -2);
|
||||
lua_pushnil(L);
|
||||
lua_settable(L, -5); // remove the category
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
return 1; // Table of unused categories/scripts/directories.
|
||||
}
|
||||
|
||||
/* int init_rules (lua_State *L)
|
||||
*
|
||||
* Arguments
|
||||
* ... All the categories/scripts/directories passed via --script
|
||||
*
|
||||
* 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
|
||||
* tags (files/directories, and possibly unused or invalid categories) are
|
||||
* then each loaded (attempted). If any do not load then an error is raised.
|
||||
*/
|
||||
int init_rules (lua_State *L)
|
||||
{
|
||||
int top = lua_gettop(L); // number of categories/scripts
|
||||
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, PORTTESTS);
|
||||
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, HOSTTESTS);
|
||||
|
||||
/* This table holds a list of all loaded script filenames, to avoid loading
|
||||
* any more than once. */
|
||||
lua_newtable(L);
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, SCRIPTFILES);
|
||||
|
||||
lua_pushcclosure(L, pick_default_categories, 0);
|
||||
lua_insert(L, 1);
|
||||
lua_call(L, top, LUA_MULTRET);
|
||||
top = lua_gettop(L); // new number of categories & scripts
|
||||
|
||||
lua_pushcclosure(L, loadcategories, 0);
|
||||
lua_insert(L, 1);
|
||||
lua_call(L, top, 1); // returns unused tags table
|
||||
|
||||
lua_pushcclosure(L, loadfile, 0);
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -3) != 0)
|
||||
{
|
||||
char path[MAX_FILENAME_LEN];
|
||||
int type = nse_fetchfile_absolute(path, sizeof(path),
|
||||
lua_tostring(L, -2));
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
lua_pushvalue(L, -2); // copy of key
|
||||
lua_pushliteral(L, SCRIPT_ENGINE_EXTENSION);
|
||||
lua_concat(L, 2);
|
||||
lua_replace(L, -2); // remove value
|
||||
type = nse_fetchfile_absolute(path, sizeof(path), lua_tostring(L, -1));
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case 0: // no such path
|
||||
luaL_error(L, "No such category, file or directory: '%s'",
|
||||
lua_tostring(L, -2));
|
||||
case 1: // nmap_fetchfile returned a file
|
||||
if (!nse_check_extension(SCRIPT_ENGINE_EXTENSION, path))
|
||||
{
|
||||
error("%s: Warning: Loading '%s' - the recommended file extension is '.nse'.",
|
||||
SCRIPT_ENGINE, path);
|
||||
}
|
||||
lua_pushvalue(L, -3); // loadfile closure
|
||||
lua_pushstring(L, path);
|
||||
lua_call(L, 1, 0);
|
||||
break;
|
||||
case 2: // nmap_fetchfile returned a dir
|
||||
lua_pushcclosure(L, loaddir, 0);
|
||||
lua_pushstring(L, path);
|
||||
lua_call(L, 1, 0);
|
||||
break;
|
||||
default:
|
||||
fatal("%s: In: %s:%i This should never happen.",
|
||||
SCRIPT_ENGINE, __FILE__, __LINE__);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
// Compute some stats
|
||||
SCRIPT_ENGINE_DEBUGGING(
|
||||
size_t rules_count;
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, HOSTTESTS);
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, PORTTESTS);
|
||||
rules_count = table_length(L, -2) + table_length(L, -1);
|
||||
lua_pop(L, 2);
|
||||
log_write(LOG_STDOUT, "%s: Initialized %d rules\n", SCRIPT_ENGINE, rules_count);
|
||||
)
|
||||
return 0;
|
||||
}
|
||||
20
nse_init.h
20
nse_init.h
@@ -1,20 +0,0 @@
|
||||
#ifndef NSE_INIT
|
||||
#define NSE_INIT
|
||||
|
||||
// initialize the lua state
|
||||
// opens the standard libraries and the nmap lua library
|
||||
int init_lua(lua_State* L);
|
||||
|
||||
//takes the script arguments provided to nmap through --script-args and
|
||||
//processes and checks them - leaves the processed string on the stack
|
||||
int init_parseargs(lua_State* L);
|
||||
//sets the previously parsed args inside nmap.registry
|
||||
int init_setargs(lua_State* L);
|
||||
|
||||
// you give it a description of scripts to run and it
|
||||
// populates the tables 'hosttests' and 'porttests' in l with
|
||||
// activation records for tests
|
||||
int init_rules(lua_State *L);
|
||||
int init_updatedb(lua_State* L);
|
||||
|
||||
#endif
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef NSE_MACROS
|
||||
#define NSE_MACROS
|
||||
|
||||
#define NSE_WAITING_TO_RUNNING "WAITING_TO_RUNNING"
|
||||
|
||||
#define HOSTRULE "hostrule"
|
||||
#define HOSTTESTS "hosttests"
|
||||
#define PORTRULE "portrule"
|
||||
|
||||
1130
nse_main.cc
1130
nse_main.cc
File diff suppressed because it is too large
Load Diff
14
nse_main.h
14
nse_main.h
@@ -9,6 +9,7 @@
|
||||
|
||||
extern "C" {
|
||||
#include "lua.h"
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
}
|
||||
|
||||
@@ -27,17 +28,14 @@ class ScriptResult
|
||||
typedef std::vector<ScriptResult> ScriptResults;
|
||||
|
||||
class Target;
|
||||
int script_scan(std::vector<Target *> &targets);
|
||||
|
||||
int script_updatedb();
|
||||
void script_scan_free();
|
||||
|
||||
//parses the arguments provided to scripts via nmap's --script-args option
|
||||
int script_check_args();
|
||||
|
||||
int process_waiting2running(lua_State *, int);
|
||||
|
||||
/* Useful auxiliary functions */
|
||||
size_t table_length(lua_State *, int);
|
||||
void nse_restore (lua_State *, int);
|
||||
|
||||
int open_nse (void);
|
||||
int script_scan(std::vector<Target *> &targets);
|
||||
void close_nse (void);
|
||||
|
||||
#endif
|
||||
|
||||
525
nse_main.lua
Normal file
525
nse_main.lua
Normal file
@@ -0,0 +1,525 @@
|
||||
-- Arguments when this file (function) is called, accessible via ...
|
||||
-- [1] The NSE C library. This is saved in the local variable cnse for
|
||||
-- access throughout the file.
|
||||
-- [2] The list of categories/files/directories passed via --script.
|
||||
-- The actual arguments passed to the anonymous main function:
|
||||
-- [1] The list of hosts we run against.
|
||||
--
|
||||
-- A few notes about the safety of the engine, that is, the ability for
|
||||
-- a script developer to crash or otherwise stall NSE. The purpose of noting
|
||||
-- these attack vectors is more to show the difficulty in accidently
|
||||
-- breaking the system than to indicate a user may wish to break the
|
||||
-- system through these means.
|
||||
-- - A script writer can use the undocumented Lua function newproxy
|
||||
-- to inject __gc code that could run (and error) at any location.
|
||||
-- - A script writer can use the debug library to break out of
|
||||
-- the "sandbox" we give it. This is made a little more difficult by
|
||||
-- our use of locals to all Lua functions we use and the exclusion
|
||||
-- of the main thread and subsequent user threads.
|
||||
-- - A simple while true do end loop can stall the system. This can be
|
||||
-- avoided by debug hooks to yield the thread at periodic intervals
|
||||
-- (and perhaps kill the thread) but a C function like string.find and
|
||||
-- a malicious pattern can stall the system from C just as easily.
|
||||
-- - The garbage collector function is available to users and they may
|
||||
-- cause the system to stall through improper use.
|
||||
-- - Of course the os and io library can cause the system to also break.
|
||||
|
||||
local NAME = "NSE";
|
||||
|
||||
local _R = debug.getregistry(); -- The registry
|
||||
local _G = _G;
|
||||
|
||||
local assert = assert;
|
||||
local collectgarbage = collectgarbage;
|
||||
local error = error;
|
||||
local getfenv = getfenv;
|
||||
local ipairs = ipairs;
|
||||
local loadfile = loadfile;
|
||||
local loadstring = loadstring;
|
||||
local next = next;
|
||||
local pairs = pairs;
|
||||
local pcall = pcall;
|
||||
local rawget = rawget;
|
||||
local select = select;
|
||||
local setfenv = setfenv;
|
||||
local setmetatable = setmetatable;
|
||||
local tonumber = tonumber;
|
||||
local tostring = tostring;
|
||||
local type = type;
|
||||
local unpack = unpack;
|
||||
|
||||
local create = coroutine.create;
|
||||
local resume = coroutine.resume;
|
||||
local running = coroutine.running;
|
||||
local status = coroutine.status;
|
||||
local yield = coroutine.yield;
|
||||
|
||||
local traceback = debug.traceback;
|
||||
|
||||
local write = io.write;
|
||||
|
||||
local ceil = math.ceil;
|
||||
|
||||
local byte = string.byte;
|
||||
local format = string.format;
|
||||
local find = string.find;
|
||||
local gsub = string.gsub;
|
||||
local lower = string.lower;
|
||||
local match = string.match;
|
||||
|
||||
local insert = table.insert;
|
||||
local remove = table.remove;
|
||||
local sort = table.sort;
|
||||
|
||||
local nmap = require "nmap";
|
||||
|
||||
local cnse, rules = ...; -- The NSE C library and Script Rules
|
||||
|
||||
do -- Append the nselib directory to the Lua search path
|
||||
local t, path = assert(cnse.fetchfile_absolute("nselib/"));
|
||||
assert(t == "directory", "could not locate nselib directory!");
|
||||
package.path = package.path..";"..path.."?.lua";
|
||||
end
|
||||
|
||||
-- Some local helper functions --
|
||||
|
||||
local log_write, verbosity, debugging =
|
||||
nmap.log_write, nmap.verbosity, nmap.debugging;
|
||||
|
||||
local function print_verbose (level, fmt, ...)
|
||||
if verbosity() >= assert(tonumber(level)) or debugging() > 0 then
|
||||
log_write("stdout", format(fmt, ...));
|
||||
end
|
||||
end
|
||||
|
||||
local function print_debug (level, fmt, ...)
|
||||
if debugging() >= assert(tonumber(level)) then
|
||||
log_write("stdout", format(fmt, ...));
|
||||
end
|
||||
end
|
||||
|
||||
local function log_error (fmt, ...)
|
||||
log_write("stderr", format(fmt, ...));
|
||||
end
|
||||
|
||||
local function table_size (t)
|
||||
local n = 0; for _ in pairs(t) do n = n + 1; end return n;
|
||||
end
|
||||
|
||||
-- recursively copy a table, for host/port tables
|
||||
-- not very rigorous, but it doesn't need to be
|
||||
local function tcopy (t)
|
||||
local tc = {};
|
||||
for k,v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
tc[k] = tcopy(v);
|
||||
else
|
||||
tc[k] = v;
|
||||
end
|
||||
end
|
||||
return tc;
|
||||
end
|
||||
|
||||
local Script = {}; -- The Script Class, its constructor is Script.new.
|
||||
local Thread = {}; -- The Thread Class, its constructor is Script:new_thread.
|
||||
do
|
||||
-- Thread:d()
|
||||
-- Outputs debug information at level 1 or higher.
|
||||
-- Changes "%THREAD" with an appropriate identifier for the debug level
|
||||
function Thread:d (fmt, ...)
|
||||
if debugging() > 1 then
|
||||
print_debug(1, gsub(fmt, "%%THREAD", self.info), ...);
|
||||
else
|
||||
print_debug(1, gsub(fmt, "%%THREAD", self.short_basename), ...);
|
||||
end
|
||||
end
|
||||
|
||||
-- thread = Script:new_thread(rule, ...)
|
||||
-- Creates a new thread for the script Script.
|
||||
-- Arguments:
|
||||
-- rule The rule argument the rule, hostrule or portrule, tested.
|
||||
-- ... The arguments passed to the rule function (host[, port]).
|
||||
-- Returns:
|
||||
-- thread The thread (class) is returned, or nil.
|
||||
function Script:new_thread (rule, ...)
|
||||
assert(rule == "hostrule" or rule == "portrule");
|
||||
if not self[rule] then return nil end -- No rule for this script?
|
||||
local file_closure = self.file_closure;
|
||||
local env = setmetatable({
|
||||
runlevel = 1,
|
||||
filename = self.filename,
|
||||
}, {__index = _G});
|
||||
setfenv(file_closure, env);
|
||||
local function main (...)
|
||||
file_closure(); -- loads script globals
|
||||
return env.action(yield(env[rule](...)));
|
||||
end
|
||||
setfenv(main, env);
|
||||
-- This thread allows us to load the script's globals in the
|
||||
-- same Lua thread the action and rule functions will execute in.
|
||||
local co = create(main);
|
||||
local s, rule_return = resume(co, ...);
|
||||
if s and rule_return then
|
||||
local thread = setmetatable({
|
||||
co = co,
|
||||
env = env,
|
||||
runlevel = ceil(tonumber(rawget(env, "runlevel")) or 1),
|
||||
identifier = tostring(co),
|
||||
info = format("'%s' (%s)", self.short_basename, tostring(co));
|
||||
type = rule == "hostrule" and "host" or "port",
|
||||
}, {
|
||||
__metatable = Thread,
|
||||
__index = function (thread, k) return Thread[k] or self[k] end
|
||||
}); -- Access to the parent Script
|
||||
thread.parent = thread; -- itself
|
||||
return thread;
|
||||
elseif not s then
|
||||
print_debug(1, "a thread for %s failed to load:\n%s\n", self.filename,
|
||||
traceback(co, tostring(rule_return)));
|
||||
end
|
||||
return nil;
|
||||
end
|
||||
|
||||
local required_fields = {
|
||||
description = "string",
|
||||
action = "function",
|
||||
categories = "table",
|
||||
};
|
||||
-- script = Script.new(filename)
|
||||
-- Creates a new Script Class for the script.
|
||||
-- Arguments:
|
||||
-- filename The filename (path) of the script to load.
|
||||
-- Returns:
|
||||
-- script The script (class) created.
|
||||
function Script.new (filename)
|
||||
assert(type(filename) == "string", "string expected");
|
||||
if not find(filename, "%.nse$") then
|
||||
log_error("Warning: Loading '"..filename..
|
||||
"' - the recommended file extension is '.nse'.");
|
||||
end
|
||||
local file_closure = assert(loadfile(filename));
|
||||
-- Give the closure its own environment, with global access
|
||||
local env = setmetatable({}, {__index = _G});
|
||||
setfenv(file_closure, env);
|
||||
local co = create(file_closure); -- Create a garbage thread
|
||||
assert(resume(co)); -- Get the globals it loads in env
|
||||
-- Check that all the required fields were set
|
||||
for f, t in pairs(required_fields) do
|
||||
local field = rawget(env, f);
|
||||
if field == nil then
|
||||
error(filename.." is missing required field: '"..f.."'");
|
||||
elseif type(field) ~= t then
|
||||
error(filename.." field '"..f.."' is of improper type '"..
|
||||
type(field).."', expected type '"..t.."'");
|
||||
end
|
||||
end
|
||||
-- Check one of two required rule functions exists
|
||||
local hostrule, portrule = rawget(env, "hostrule"), rawget(env, "portrule");
|
||||
assert(type(hostrule) == "function" or type(portrule) == "function",
|
||||
filename.." is missing a required function: 'hostrule' or 'portrule'");
|
||||
-- Assert that categories is an array of strings
|
||||
for i, category in ipairs(rawget(env, "categories")) do
|
||||
assert(type(category) == "string",
|
||||
filename.." has non-string entries in the 'categories' array");
|
||||
end
|
||||
-- Return the script
|
||||
return setmetatable({
|
||||
filename = filename,
|
||||
basename = match(filename, "[/\\]([^/\\]-)$") or filename,
|
||||
short_basename = match(filename, "[/\\]([^/\\]-)%.nse$") or
|
||||
match(filename, "[/\\]([^/\\]-)%.[^.]*$") or
|
||||
filename,
|
||||
id = match(filename, "^.-[/\\]([^\\/]-)%.nse$") or filename,
|
||||
file_closure = file_closure,
|
||||
hostrule = type(hostrule) == "function" and hostrule or nil,
|
||||
portrule = type(portrule) == "function" and portrule or nil,
|
||||
args = {n = 0};
|
||||
categories = rawget(env, "categories"),
|
||||
author = rawget(env, "author"),
|
||||
license = rawget(env, "license"),
|
||||
runlevel = ceil(tonumber(rawget(env, "runlevel")) or 1),
|
||||
threads = {},
|
||||
}, {__index = Script, __metatable = Script});
|
||||
end
|
||||
end
|
||||
|
||||
-- check_rules(rules)
|
||||
-- Ensures reserved rules are not explicitly specified.
|
||||
-- Adds the "default" category if no rules were specified.
|
||||
-- Adds reserved rules that were internally specified (--sV for "version").
|
||||
--
|
||||
-- Arguments:
|
||||
-- rules The array of rules to check.
|
||||
local function check_rules (rules)
|
||||
local reserved = {
|
||||
version = not not cnse.scriptversion,
|
||||
};
|
||||
for i, rule in ipairs(rules) do
|
||||
if reserved[lower(rule)] ~= nil then
|
||||
error("explicitly specifying rule '"..rule.."' is prohibited");
|
||||
end
|
||||
end
|
||||
if cnse.default and #rules == 0 then rules[1] = "default"; end
|
||||
for rule, option in pairs(reserved) do
|
||||
if option then rules[#rules+1] = rule; end
|
||||
end
|
||||
end
|
||||
|
||||
-- chosen_scripts = get_chosen_scripts(rules)
|
||||
-- Loads all the scripts for the given rules using the Script Database.
|
||||
-- Arguments:
|
||||
-- rules The array of rules to use for loading scripts.
|
||||
-- Returns:
|
||||
-- chosen_scripts The array of scripts loaded for the given rules.
|
||||
local function get_chosen_scripts (rules)
|
||||
check_rules(rules);
|
||||
|
||||
local script_dbpath = cnse.script_dbpath;
|
||||
local t, path = cnse.fetchfile_absolute(script_dbpath);
|
||||
if not t then
|
||||
print_verbose(1, "Creating non-existent script database.");
|
||||
assert(cnse.updatedb(), "could not update script database!");
|
||||
t, path = assert(cnse.fetchfile_absolute(script_dbpath));
|
||||
end
|
||||
local db_closure = assert(loadfile(path));
|
||||
|
||||
local chosen_scripts, entry_rules, files_loaded = {}, {}, {};
|
||||
|
||||
-- Initialize entry_rules with the list of rules provided by the user.
|
||||
-- Each element of entry_rules may refer to another canonical element.
|
||||
-- Here the lower-case rule points to the potentially mixed-case rule
|
||||
-- provided by the user.
|
||||
for i, rule in ipairs(rules) do
|
||||
entry_rules[lower(rule)] = rule;
|
||||
entry_rules[rule] = false;
|
||||
end
|
||||
|
||||
-- Start by loading scripts by category. This function is run on each
|
||||
-- Entry in script.db.
|
||||
local function entry (script_entry)
|
||||
local category, filename = script_entry.category, script_entry.filename;
|
||||
assert(type(category) == "string" and type(filename) == "string");
|
||||
|
||||
-- Don't load a file more than once.
|
||||
if files_loaded[filename] then return end
|
||||
|
||||
-- Do we have a rule for this category (or an "all" rule)?
|
||||
if entry_rules[category] ~= nil or
|
||||
entry_rules.all ~= nil and category ~= "version" then
|
||||
local index = entry_rules[category] ~= nil and category or "all";
|
||||
local mark = entry_rules[index];
|
||||
-- mark may point to the actual mixed case category passed via command
|
||||
-- line
|
||||
if type(mark) == "boolean" then
|
||||
entry_rules[index] = true;
|
||||
else
|
||||
entry_rules[mark] = true;
|
||||
end
|
||||
local t, path = cnse.fetchfile_absolute(filename);
|
||||
assert(t == "file", filename.." is not a file!");
|
||||
chosen_scripts[#chosen_scripts+1] = Script.new(path);
|
||||
files_loaded[filename] = true;
|
||||
end
|
||||
end
|
||||
|
||||
setfenv(db_closure, {Entry = entry});
|
||||
db_closure(); -- Load the scripts
|
||||
|
||||
-- Now load any scripts listed by name rather than by category.
|
||||
for rule, loaded in pairs(entry_rules) do
|
||||
if not loaded then -- attempt to load the file/directory
|
||||
local t, path = cnse.fetchfile_absolute(rule);
|
||||
if t == nil then -- perhaps omitted the extension?
|
||||
t, path = cnse.fetchfile_absolute(rule..".nse");
|
||||
end
|
||||
if t == nil then
|
||||
error("No such category, filename or directory: '"..rule.."'");
|
||||
elseif t == "file" and not files_loaded[path] then
|
||||
chosen_scripts[#chosen_scripts+1] = Script.new(path);
|
||||
files_loaded[path] = true;
|
||||
elseif t == "directory" then
|
||||
for i, file in ipairs(cnse.dump_dir(path)) do
|
||||
if not files_loaded[file] then
|
||||
chosen_scripts[#chosen_scripts+1] = Script.new(file);
|
||||
files_loaded[file] = true;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return chosen_scripts;
|
||||
end
|
||||
|
||||
-- run(threads)
|
||||
-- The main loop function for NSE. It handles running all the script threads.
|
||||
-- Arguments:
|
||||
-- threads An array of threads (a runlevel) to run.
|
||||
local function run (threads)
|
||||
-- running scripts may be resumed at any time. waiting scripts are
|
||||
-- yielded until Nsock wakes them. After being awakened with
|
||||
-- nse_restore, waiting threads become pending and later are moved all
|
||||
-- at once back to running.
|
||||
local running, waiting, pending = {}, {}, {}
|
||||
-- hosts maps a host to a list of threads for that host.
|
||||
local hosts, total = {}, 0
|
||||
local current
|
||||
local progress = cnse.scan_progress_meter(NAME);
|
||||
|
||||
print_debug(1, "NSE Script Threads (%d) running:", #threads);
|
||||
while #threads > 0 do
|
||||
local thread = remove(threads);
|
||||
thread:d("Starting %THREAD against %s.", thread.host.ip)
|
||||
running[thread.co], total = thread, total + 1;
|
||||
hosts[thread.host] = hosts[thread.host] or {};
|
||||
hosts[thread.host][thread.co] = true;
|
||||
end
|
||||
|
||||
-- This WAITING_TO_RUNNING function is called by nse_restore in
|
||||
-- nse_main.cc.
|
||||
_R.WAITING_TO_RUNNING = function (co, ...)
|
||||
if waiting[co] then -- ignore a thread not waiting
|
||||
pending[co], waiting[co] = waiting[co], nil;
|
||||
pending[co].args = {n = select("#", ...), ...};
|
||||
end
|
||||
end
|
||||
|
||||
-- Loop while any thread is running or waiting.
|
||||
while next(running) or next(waiting) do
|
||||
local nr, nw = table_size(running), table_size(waiting);
|
||||
cnse.nsock_loop(50); -- Allow nsock to perform any pending callbacks
|
||||
if cnse.key_was_pressed() then
|
||||
print_verbose(1, "Active NSE Script Threads: %d (%d waiting)\n",
|
||||
nr+nw, nw);
|
||||
progress("printStats", 1-(nr+nw)/total);
|
||||
elseif progress "mayBePrinted" then
|
||||
if verbosity() > 1 or debugging() > 0 then
|
||||
progress("printStats", 1-(nr+nw)/total);
|
||||
else
|
||||
progress("printStatsIfNecessary", 1-(nr+nw)/total);
|
||||
end
|
||||
end
|
||||
|
||||
-- Checked for timed-out hosts.
|
||||
for co, thread in pairs(waiting) do
|
||||
if cnse.timedOut(thread.host) then
|
||||
waiting[co] = nil;
|
||||
thread:d("%THREAD target timed out");
|
||||
end
|
||||
end
|
||||
|
||||
for co, thread in pairs(running) do
|
||||
current, running[co] = thread, nil;
|
||||
cnse.startTimeOutClock(thread.host);
|
||||
|
||||
local s, result = resume(co, unpack(thread.args, 1, thread.args.n));
|
||||
if not s then -- script error...
|
||||
hosts[thread.host][co] = nil;
|
||||
thread:d("%THREAD threw an error!\n%s\n",
|
||||
traceback(co, tostring(result)));
|
||||
elseif status(co) == "suspended" then
|
||||
waiting[co] = thread;
|
||||
elseif status(co) == "dead" then
|
||||
hosts[thread.host][co] = nil;
|
||||
if type(result) == "string" then
|
||||
-- Escape any character outside the range 32-126 except for tab,
|
||||
-- carriage return, and line feed. This makes the string safe for
|
||||
-- screen display as well as XML (see section 2.2 of the XML spec).
|
||||
result = gsub(result, "[^\t\r\n\032-\126]", function(a)
|
||||
return format("\\x%02X", byte(a));
|
||||
end);
|
||||
if thread.type == "host" then
|
||||
cnse.host_set_output(thread.host, thread.id, result);
|
||||
else
|
||||
cnse.port_set_output(thread.host, thread.port, thread.id, result);
|
||||
end
|
||||
end
|
||||
thread:d("Finished %THREAD against %s", thread.host.ip);
|
||||
end
|
||||
|
||||
-- Any more threads running for this host?
|
||||
if not next(hosts[thread.host]) then
|
||||
cnse.stopTimeOutClock(thread.host);
|
||||
end
|
||||
end
|
||||
|
||||
-- Move pending threads back to running.
|
||||
for co, thread in pairs(pending) do
|
||||
pending[co], running[co] = nil, thread;
|
||||
end
|
||||
|
||||
collectgarbage "collect"; -- important for collecting used sockets & proxies
|
||||
end
|
||||
|
||||
progress "endTask";
|
||||
end
|
||||
|
||||
do -- Load script arguments
|
||||
local args = gsub((cnse.scriptargs or ""), "=([%w_]+)", "=\"%1\"");
|
||||
local argsf, err = loadstring("return {"..args.."}", "Script Arguments");
|
||||
if not argsf then
|
||||
error("failed to parse --script-args:\n"..args.."\n"..err);
|
||||
else
|
||||
nmap.registry.args = argsf();
|
||||
end
|
||||
end
|
||||
|
||||
-- Load all user chosen scripts
|
||||
local chosen_scripts = get_chosen_scripts(rules);
|
||||
print_verbose(1, "Loaded %d scripts for scanning.", #chosen_scripts);
|
||||
for i, script in ipairs(chosen_scripts) do
|
||||
print_debug(2, "Loaded '%s'.", script.basename);
|
||||
end
|
||||
|
||||
-- main(hosts)
|
||||
-- This is the main function we return to NSE (on the C side) which actually
|
||||
-- runs a scan against an array of hosts. nse_main.cc gets this function
|
||||
-- by calling loadfile on nse_main.lua.
|
||||
-- Arguments:
|
||||
-- hosts An array of hosts to scan.
|
||||
return function (hosts)
|
||||
if #hosts > 1 then
|
||||
print_verbose(1, "Script scanning %d hosts.", #hosts);
|
||||
elseif #hosts == 1 then
|
||||
print_verbose(1, "Script scanning %s.", hosts[1].ip);
|
||||
end
|
||||
|
||||
-- Set up the runlevels.
|
||||
local threads, runlevels = {}, {};
|
||||
for j, host in ipairs(hosts) do
|
||||
-- Check hostrules for this host.
|
||||
for i, script in ipairs(chosen_scripts) do
|
||||
local thread = script:new_thread("hostrule", tcopy(host));
|
||||
if thread then
|
||||
local runlevel = thread.runlevel;
|
||||
if threads[runlevel] == nil then insert(runlevels, runlevel); end
|
||||
threads[runlevel] = threads[runlevel] or {};
|
||||
insert(threads[runlevel], thread);
|
||||
thread.args, thread.host = {n = 1, tcopy(host)}, host;
|
||||
end
|
||||
end
|
||||
-- Check portrules for this host.
|
||||
for port in cnse.ports(host) do
|
||||
for i, script in ipairs(chosen_scripts) do
|
||||
local thread = script:new_thread("portrule", tcopy(host),
|
||||
tcopy(port));
|
||||
if thread then
|
||||
local runlevel = thread.runlevel;
|
||||
if threads[runlevel] == nil then insert(runlevels, runlevel); end
|
||||
threads[runlevel] = threads[runlevel] or {};
|
||||
insert(threads[runlevel], thread);
|
||||
thread.args, thread.host, thread.port =
|
||||
{n = 2, tcopy(host), tcopy(port)}, host, port;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
sort(runlevels);
|
||||
for i, runlevel in ipairs(runlevels) do
|
||||
print_verbose(1, "Starting runlevel %s scan", tostring(runlevel));
|
||||
run(threads[runlevel]);
|
||||
end
|
||||
|
||||
collectgarbage "collect";
|
||||
print_verbose(1, "Script Scanning completed.");
|
||||
end
|
||||
@@ -243,7 +243,10 @@ static int aux_mutex (lua_State *L)
|
||||
lua_call(L, 2, 1);
|
||||
lua_replace(L, lua_upvalueindex(2));
|
||||
if (!lua_isnil(L, lua_upvalueindex(2))) // waiting threads had a thread
|
||||
process_waiting2running(lua_tothread(L, lua_upvalueindex(2)), 0);
|
||||
{
|
||||
assert(lua_isthread(L, lua_upvalueindex(2)));
|
||||
nse_restore(lua_tothread(L, lua_upvalueindex(2)), 0);
|
||||
}
|
||||
return 0;
|
||||
case 2: // trylock
|
||||
if (lua_isnil(L, lua_upvalueindex(2)))
|
||||
@@ -281,7 +284,7 @@ static int l_mutex (lua_State *L)
|
||||
return 1; // aux_mutex closure
|
||||
}
|
||||
|
||||
static Target *get_target (lua_State *L, int index)
|
||||
Target *get_target (lua_State *L, int index)
|
||||
{
|
||||
Target *target;
|
||||
luaL_checktype(L, index, LUA_TTABLE);
|
||||
@@ -298,7 +301,7 @@ static Target *get_target (lua_State *L, int index)
|
||||
return target;
|
||||
}
|
||||
|
||||
static Port *get_port (lua_State *L, Target *target, int index)
|
||||
Port *get_port (lua_State *L, Target *target, int index)
|
||||
{
|
||||
Port *port = NULL;
|
||||
int portno, protocol;
|
||||
|
||||
@@ -8,6 +8,8 @@ int luaopen_nmap(lua_State* l);
|
||||
int luaopen_stdnse_c (lua_State *L);
|
||||
void set_hostinfo(lua_State* l, Target* currenths);
|
||||
void set_portinfo(lua_State* l, Port* port);
|
||||
Target *get_target (lua_State *L, int index);
|
||||
Port *get_port (lua_State *L, Target *target, int index);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
163
nse_nsock.cc
163
nse_nsock.cc
@@ -11,8 +11,8 @@ extern "C" {
|
||||
#include <iomanip>
|
||||
|
||||
#include "nse_nsock.h"
|
||||
#include "nse_main.h"
|
||||
#include "nse_macros.h"
|
||||
#include "nse_debug.h"
|
||||
|
||||
#include "nsock.h"
|
||||
#include "nmap_error.h"
|
||||
@@ -39,10 +39,6 @@ extern "C" {
|
||||
|
||||
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_send(lua_State *L);
|
||||
static int l_nsock_receive(lua_State *L);
|
||||
@@ -60,10 +56,10 @@ static int l_nsock_ncap_register(lua_State *L);
|
||||
static int l_nsock_pcap_receive(lua_State *L);
|
||||
|
||||
|
||||
void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
|
||||
void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
|
||||
void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
|
||||
void l_nsock_receive_buf_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
|
||||
void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *yield);
|
||||
void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *yield);
|
||||
void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *yield);
|
||||
void l_nsock_receive_buf_handler(nsock_pool nsp, nsock_event nse, void *yield);
|
||||
|
||||
int l_nsock_check_buf(lua_State *L);
|
||||
|
||||
@@ -73,6 +69,24 @@ void l_nsock_trace(nsock_iod nsiod, const char* message, int direction);
|
||||
const char* inet_ntop_both(int af, const void* v_addr, char* ipstring);
|
||||
unsigned short inet_port_both(int af, const void* v_addr);
|
||||
|
||||
/* 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 len = 0;
|
||||
lua_pushvalue(L, index);
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0)
|
||||
{
|
||||
len++;
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1); // table
|
||||
return len;
|
||||
}
|
||||
|
||||
static std::string hexify (const char *str, size_t len)
|
||||
{
|
||||
@@ -142,7 +156,7 @@ static int proxy_gc (lua_State *L)
|
||||
if (lua_next(L, -2) != 0)
|
||||
{
|
||||
lua_State *thread = lua_tothread(L, -2);
|
||||
process_waiting2running(thread, 0);
|
||||
nse_restore(thread, 0);
|
||||
lua_pushnil(L);
|
||||
lua_replace(L, -2); // replace boolean
|
||||
lua_settable(L, -3); // remove thread from waiting
|
||||
@@ -167,7 +181,6 @@ static void new_proxy (lua_State *L)
|
||||
*/
|
||||
static int socket_lock (lua_State *L)
|
||||
{
|
||||
luaL_checkudata(L, 1, "nsock");
|
||||
lua_settop(L, 1);
|
||||
lua_rawgeti(L, LUA_ENVIRONINDEX, THREAD_PROXY);
|
||||
lua_pushthread(L);
|
||||
@@ -190,7 +203,7 @@ static int socket_lock (lua_State *L)
|
||||
lua_pushboolean(L, true);
|
||||
lua_settable(L, -3);
|
||||
lua_pop(L, 1); // CONNECT_WAITING
|
||||
lua_pushboolean(L, false);
|
||||
return lua_yield(L, 0);
|
||||
} else
|
||||
{
|
||||
// There is room for this thread to open sockets. Make a new proxy userdata
|
||||
@@ -240,13 +253,18 @@ struct ncap_socket{
|
||||
* address this structure. */
|
||||
};
|
||||
|
||||
struct yield {
|
||||
lua_State *thread; /* thread to resume */
|
||||
struct l_nsock_udata *udata; /* self reference */
|
||||
} yield;
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
struct ncap_request{
|
||||
int suspended; /* is the thread suspended? (lua_yield) */
|
||||
lua_State *L; /* lua_State of current process
|
||||
* or NULL if process isn't suspended */
|
||||
//lua_State *L; /* lua_State of current process or NULL if process isn't suspended */
|
||||
struct yield *yield;
|
||||
nsock_event_id nseid; /* nse for this specific lua_State */
|
||||
struct timeval end_time;
|
||||
char *key; /* (free) zero-terminated key used in map to
|
||||
@@ -269,14 +287,15 @@ struct ncap_request{
|
||||
* we need to use this. */
|
||||
};
|
||||
|
||||
|
||||
struct l_nsock_udata {
|
||||
int timeout;
|
||||
nsock_iod nsiod;
|
||||
void *ssl_session;
|
||||
struct yield yield;
|
||||
/*used for buffered reading */
|
||||
int bufidx; /*index inside lua's registry */
|
||||
int bufused;
|
||||
int rbuf_args[3]; /* indices in lua registry for receive_buf args */
|
||||
|
||||
struct ncap_socket *ncap_socket;
|
||||
struct ncap_request *ncap_request;
|
||||
@@ -295,11 +314,9 @@ int luaopen_nsock (lua_State *L)
|
||||
* connect function.
|
||||
*/
|
||||
static const char connect[] =
|
||||
"local yield, connect, socket_lock = ...;\n"
|
||||
"local connect, socket_lock = ...;\n"
|
||||
"return function(socket, ...)\n"
|
||||
" while not socket_lock(socket) do\n"
|
||||
" yield();\n"
|
||||
" end\n"
|
||||
" repeat until socket_lock(socket) == true;\n"
|
||||
" return connect(socket, ...);\n"
|
||||
"end";
|
||||
|
||||
@@ -354,12 +371,9 @@ int luaopen_nsock (lua_State *L)
|
||||
/* Load the connect function */
|
||||
if (luaL_loadstring(L, connect) != 0)
|
||||
fatal("connect did not compile!");
|
||||
lua_getglobal(L, "coroutine");
|
||||
lua_getfield(L, -1, "yield");
|
||||
lua_replace(L, -2); // remove coroutine table
|
||||
lua_pushcclosure(L, l_nsock_connect, 0);
|
||||
lua_pushcclosure(L, socket_lock, 0);
|
||||
lua_call(L, 3, 1); // leave connect function on stack...
|
||||
lua_call(L, 2, 1); // leave connect function on stack...
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
lua_setfenv(L, -2); // set the connect function's environment to _G
|
||||
|
||||
@@ -395,6 +409,9 @@ int l_nsock_new(lua_State *L) {
|
||||
udata->timeout = DEFAULT_TIMEOUT;
|
||||
udata->bufidx = LUA_NOREF;
|
||||
udata->bufused= 0;
|
||||
udata->rbuf_args[0] = LUA_NOREF;
|
||||
udata->rbuf_args[1] = LUA_NOREF;
|
||||
udata->rbuf_args[2] = LUA_NOREF;
|
||||
udata->ncap_socket = NULL;
|
||||
udata->ncap_request = NULL;
|
||||
udata->ncap_cback_ref = 0;
|
||||
@@ -473,18 +490,18 @@ static int l_nsock_connect(lua_State *L) {
|
||||
case 't':
|
||||
if (strcmp(how, "tcp")) goto error;
|
||||
nsock_connect_tcp(nsp, udata->nsiod, l_nsock_connect_handler,
|
||||
udata->timeout, L, dest->ai_addr, dest->ai_addrlen, port);
|
||||
udata->timeout, &udata->yield, dest->ai_addr, dest->ai_addrlen, port);
|
||||
break;
|
||||
case 'u':
|
||||
if (strcmp(how, "udp")) goto error;
|
||||
nsock_connect_udp(nsp, udata->nsiod, l_nsock_connect_handler,
|
||||
L, dest->ai_addr, dest->ai_addrlen, port);
|
||||
&udata->yield, dest->ai_addr, dest->ai_addrlen, port);
|
||||
break;
|
||||
case 's':
|
||||
if (strcmp(how, "ssl")) goto error;
|
||||
#ifdef HAVE_OPENSSL
|
||||
nsock_connect_ssl(nsp, udata->nsiod, l_nsock_connect_handler,
|
||||
udata->timeout, L, dest->ai_addr, dest->ai_addrlen, port,
|
||||
udata->timeout, &udata->yield, dest->ai_addr, dest->ai_addrlen, port,
|
||||
udata->ssl_session);
|
||||
break;
|
||||
#else
|
||||
@@ -498,6 +515,7 @@ static int l_nsock_connect(lua_State *L) {
|
||||
}
|
||||
|
||||
freeaddrinfo(dest);
|
||||
udata->yield.thread = L;
|
||||
return lua_yield(L, 0);
|
||||
|
||||
error:
|
||||
@@ -506,17 +524,18 @@ error:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
||||
lua_State *L = (lua_State*) lua_state;
|
||||
void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *yield) {
|
||||
struct yield *y = (struct yield *) yield;
|
||||
lua_State *L = y->thread;
|
||||
|
||||
if(o.scriptTrace()) {
|
||||
l_nsock_trace(nse_iod(nse), "CONNECT", TO);
|
||||
}
|
||||
|
||||
if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) {
|
||||
process_waiting2running((lua_State*) lua_state, 1);
|
||||
nse_restore(y->thread, 1);
|
||||
} else {
|
||||
process_waiting2running((lua_State*) lua_state, 2);
|
||||
nse_restore(y->thread, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,17 +555,19 @@ static int l_nsock_send(lua_State *L) {
|
||||
if(o.scriptTrace())
|
||||
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);
|
||||
nsock_write(nsp, udata->nsiod, l_nsock_send_handler, udata->timeout, &udata->yield, string, string_len);
|
||||
udata->yield.thread = L;
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
|
||||
void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
||||
lua_State *L = (lua_State*) lua_state;
|
||||
void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *yield) {
|
||||
struct yield *y = (struct yield *) yield;
|
||||
lua_State *L = y->thread;
|
||||
|
||||
if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) {
|
||||
process_waiting2running((lua_State*) lua_state, 1);
|
||||
nse_restore(y->thread, 1);
|
||||
} else {
|
||||
process_waiting2running((lua_State*) lua_state, 2);
|
||||
nse_restore(y->thread, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,8 +581,9 @@ static int l_nsock_receive(lua_State *L) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
nsock_read(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, L);
|
||||
nsock_read(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, &udata->yield);
|
||||
|
||||
udata->yield.thread = L;
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
|
||||
@@ -577,8 +599,10 @@ static int l_nsock_receive_lines(lua_State *L) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
nsock_readlines(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, L, nlines);
|
||||
nsock_readlines(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, &udata->yield, nlines);
|
||||
|
||||
|
||||
udata->yield.thread = L;
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
|
||||
@@ -594,13 +618,15 @@ static int l_nsock_receive_bytes(lua_State *L) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
nsock_readbytes(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, L, nbytes);
|
||||
nsock_readbytes(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, &udata->yield, nbytes);
|
||||
|
||||
udata->yield.thread = L;
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
|
||||
void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
||||
lua_State *L = (lua_State*) lua_state;
|
||||
void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *yield) {
|
||||
struct yield *y = (struct yield *) yield;
|
||||
lua_State *L = y->thread;
|
||||
char* rcvd_string;
|
||||
int rcvd_len = 0;
|
||||
|
||||
@@ -611,9 +637,9 @@ void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
||||
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);
|
||||
nse_restore(y->thread, 2);
|
||||
} else {
|
||||
process_waiting2running((lua_State*) lua_state, 2);
|
||||
nse_restore(y->thread, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -726,6 +752,8 @@ static int l_nsock_gc(lua_State *L){
|
||||
return 0;
|
||||
}else{
|
||||
//FIXME - check wheter close returned true!!
|
||||
for (int i = 0; i < 3; i++)
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, udata->rbuf_args[i]);
|
||||
l_nsock_close(L);
|
||||
}
|
||||
return 0;
|
||||
@@ -779,9 +807,11 @@ static int l_nsock_set_timeout(lua_State *L) {
|
||||
/* buffered I/O */
|
||||
static int l_nsock_receive_buf(lua_State *L) {
|
||||
l_nsock_udata* udata = (l_nsock_udata*) luaL_checkudata(L, 1, "nsock");
|
||||
if(lua_gettop(L)==2){
|
||||
/*we were called with 2 arguments only - push the default third one*/
|
||||
lua_pushboolean(L,true);
|
||||
lua_settop(L, 3);
|
||||
udata->yield.udata = udata;
|
||||
for (int i = 0; i < 3; i++) { /* compatibility, clean up someday */
|
||||
lua_pushvalue(L, i+1); /* argument 1-3 */
|
||||
udata->rbuf_args[i] = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
if(udata->nsiod == NULL) {
|
||||
lua_pushboolean(L, false);
|
||||
@@ -792,7 +822,7 @@ static int l_nsock_receive_buf(lua_State *L) {
|
||||
lua_pushstring(L,"");
|
||||
udata->bufidx = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
udata->bufused=1;
|
||||
nsock_read(nsp, udata->nsiod, l_nsock_receive_buf_handler, udata->timeout, L);
|
||||
nsock_read(nsp, udata->nsiod, l_nsock_receive_buf_handler, udata->timeout, &udata->yield);
|
||||
}else if(udata->bufused==-1){ /*error message is inside the buffer*/
|
||||
lua_pushboolean(L,false);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, udata->bufidx);
|
||||
@@ -804,20 +834,29 @@ static int l_nsock_receive_buf(lua_State *L) {
|
||||
/*if we didn't have enough data in the buffer another nsock_read()
|
||||
* was scheduled - its callback will put us in running state again
|
||||
*/
|
||||
return lua_yield(L,3);
|
||||
udata->yield.thread = L;
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
/*yielding with 3 arguments since we need them when the callback arrives */
|
||||
return lua_yield(L, 3);
|
||||
udata->yield.thread = L;
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
|
||||
void l_nsock_receive_buf_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
||||
lua_State *L = (lua_State*) lua_state;
|
||||
void l_nsock_receive_buf_handler(nsock_pool nsp, nsock_event nse, void *yield) {
|
||||
struct yield *y = (struct yield *) yield;
|
||||
l_nsock_udata *udata = y->udata;
|
||||
lua_State *L= y->thread;
|
||||
char* rcvd_string;
|
||||
int rcvd_len = 0;
|
||||
int tmpidx;
|
||||
l_nsock_udata* udata = (l_nsock_udata*) luaL_checkudata(L, 1, "nsock");
|
||||
/* set stack values, this all needs fixing... */
|
||||
for (int i = 0; i < 3; i++) {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, udata->rbuf_args[i]);
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, udata->rbuf_args[i]);
|
||||
udata->rbuf_args[i] = LUA_NOREF;
|
||||
}
|
||||
if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) {
|
||||
|
||||
//l_nsock_checkstatus pushes true on the stack in case of success
|
||||
@@ -841,7 +880,7 @@ void l_nsock_receive_buf_handler(nsock_pool nsp, nsock_event nse, void *lua_stat
|
||||
*/
|
||||
return;
|
||||
}
|
||||
process_waiting2running((lua_State*) lua_state, 2);
|
||||
nse_restore(y->thread, 2);
|
||||
} else {
|
||||
if(udata->bufused>1){
|
||||
/*error occured after we read into some data into the buffer
|
||||
@@ -857,9 +896,9 @@ void l_nsock_receive_buf_handler(nsock_pool nsp, nsock_event nse, void *lua_stat
|
||||
l_nsock_clear_buf(L, udata);
|
||||
udata->bufidx=tmpidx;
|
||||
udata->bufused=-1;
|
||||
process_waiting2running((lua_State*) lua_state, 2);
|
||||
nse_restore(y->thread, 2);
|
||||
}else{ /*buffer should be empty */
|
||||
process_waiting2running((lua_State*) lua_state, 2);
|
||||
nse_restore(y->thread, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -903,7 +942,7 @@ int l_nsock_check_buf(lua_State *L ){
|
||||
/*the stack contains on top the indices where we want to seperate */
|
||||
if(lua_isnil(L,-1)){ /*not found anything try to read more data*/
|
||||
lua_pop(L,2);
|
||||
nsock_read(nsp, udata->nsiod, l_nsock_receive_buf_handler, udata->timeout, L);
|
||||
nsock_read(nsp, udata->nsiod, l_nsock_receive_buf_handler, udata->timeout, &udata->yield);
|
||||
lua_pushboolean(L,keeppattern);
|
||||
return NSOCK_WRAPPER_BUFFER_MOREREAD;
|
||||
}else{
|
||||
@@ -955,7 +994,7 @@ void l_nsock_clear_buf(lua_State *L, l_nsock_udata* udata){
|
||||
static void l_nsock_sleep_handler(nsock_pool nsp, nsock_event nse, void *udata) {
|
||||
lua_State *L = (lua_State*) udata;
|
||||
assert(nse_status(nse) == NSE_STATUS_SUCCESS);
|
||||
process_waiting2running(L, 0);
|
||||
nse_restore(L, 0);
|
||||
}
|
||||
|
||||
int l_nsock_sleep(lua_State *L) {
|
||||
@@ -1196,7 +1235,7 @@ static int l_nsock_ncap_register(lua_State *L){
|
||||
|
||||
TIMEVAL_MSEC_ADD(nr->end_time, now, udata->timeout);
|
||||
nr->key = strdup(hex((char*)testdata, testdatasz));
|
||||
nr->L = L;
|
||||
nr->yield = &udata->yield;
|
||||
nr->ncap_cback_ref = udata->ncap_cback_ref;
|
||||
/* always create new event. */
|
||||
nr->nseid = nsock_pcap_read_packet(nsp,
|
||||
@@ -1238,6 +1277,7 @@ int l_nsock_pcap_receive(lua_State *L){
|
||||
/* no data yet? suspend thread */
|
||||
nr->suspended = 1;
|
||||
|
||||
udata->yield.thread = L;
|
||||
return lua_yield(L, 0);
|
||||
}
|
||||
|
||||
@@ -1272,7 +1312,7 @@ void l_nsock_pcap_receive_handler(nsock_pool nsp, nsock_event nse, void *userdat
|
||||
|
||||
switch(nse_status(nse)) {
|
||||
case NSE_STATUS_SUCCESS:{
|
||||
char *key = ncap_request_do_callback(nse, nr->L, nr->ncap_cback_ref);
|
||||
char *key = ncap_request_do_callback(nse, nr->yield->thread, nr->ncap_cback_ref);
|
||||
|
||||
/* processes threads that receive every packet */
|
||||
this_event_restored += ncap_request_set_results(nse, "");
|
||||
@@ -1383,7 +1423,7 @@ void ncap_request_set_result(nsock_event nse, struct ncap_request *nr) {
|
||||
/* if lua thread was suspended, restore it. If it wasn't, just return results
|
||||
* (push them on the stack and return) */
|
||||
int ncap_restore_lua(ncap_request *nr){
|
||||
lua_State *L = nr->L;
|
||||
lua_State *L = nr->yield->thread;
|
||||
|
||||
if(nr->r_success){
|
||||
lua_pushboolean(L, true);
|
||||
@@ -1397,7 +1437,7 @@ int ncap_restore_lua(ncap_request *nr){
|
||||
lua_pushnil(L);
|
||||
}
|
||||
bool suspended = nr->suspended;
|
||||
nr->L = NULL;
|
||||
//nr->L = NULL; // FIXME ??
|
||||
nr->ncap_cback_ref = 0; /* this ref is freed in different place (on udata->ncap_cback_ref) */
|
||||
if(nr->key) free(nr->key);
|
||||
if(nr->r_status) free(nr->r_status);
|
||||
@@ -1407,9 +1447,10 @@ int ncap_restore_lua(ncap_request *nr){
|
||||
free(nr);
|
||||
|
||||
if(suspended) /* lua process is suspended */
|
||||
return process_waiting2running(L, 4);
|
||||
nse_restore(L, 4);
|
||||
else /* not suspended, just pass output */
|
||||
return 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user