diff --git a/nse_fs.cc b/nse_fs.cc index e146f9b7e..bfce8e74e 100644 --- a/nse_fs.cc +++ b/nse_fs.cc @@ -4,7 +4,6 @@ extern "C" { #include "lauxlib.h" } -#include #include #include @@ -18,7 +17,25 @@ extern "C" { #include "nmap_error.h" #include "NmapOps.h" -#define MAX_FILENAME_LEN 4096 +#define DIR_METATABLE "dir" + +#ifndef MAXPATHLEN +# define MAXPATHLEN 2048 +#endif + +#ifndef MAX_DIR_LENGTH +# define MAX_DIR_LENGTH 1024 +#endif + +typedef struct dir_data { + int closed; +#ifdef WIN32 + long hFile; + char pattern[MAX_DIR_LENGTH+1]; +#else + DIR *dir; +#endif +} dir_data; extern NmapOps o; @@ -32,22 +49,7 @@ static bool filename_is_absolute(const char *file) { return false; } -/* This is simply the most portable way to check - * if a file has a given extension. - * The portability comes at the price of reduced - * flexibility. - */ -int nse_check_extension (const char* ext, const char* path) -{ - int pathlen = strlen(path); - int extlen = strlen(ext); - if (extlen > pathlen || pathlen > MAX_FILENAME_LEN) - return 0; - else - return strcmp(path + pathlen - extlen, ext) == 0; -} - -int nse_fetchfile(char *path, size_t path_len, const char *file) { +static int nse_fetchfile(char *path, size_t path_len, const char *file) { int type = nmap_fetchfile(path, path_len, file); // lets look in /scripts too @@ -62,7 +64,7 @@ int nse_fetchfile(char *path, size_t path_len, const char *file) { /* This is a modification of nse_fetchfile that first looks for an * absolute file name. */ -int nse_fetchfile_absolute(char *path, size_t path_len, const char *file) { +static int nse_fetchfile_absolute(char *path, size_t path_len, const char *file) { if (filename_is_absolute(file)) { if (o.debugging > 1) log_write(LOG_STDOUT, "%s: Trying absolute path %s\n", SCRIPT_ENGINE, file); @@ -73,124 +75,159 @@ int nse_fetchfile_absolute(char *path, size_t path_len, const char *file) { return nse_fetchfile(path, path_len, file); } -#ifdef WIN32 - -int nse_scandir (lua_State *L) { - HANDLE dir; - WIN32_FIND_DATA entry; - std::string path; - BOOL morefiles = FALSE; - const char *dirname = luaL_checkstring(L, 1); - int files_or_dirs = luaL_checkint(L, 2); - - lua_createtable(L, 100, 0); // 100 files average - - dir = FindFirstFile((std::string(dirname) + "\\*").c_str(), &entry); - - if (dir == INVALID_HANDLE_VALUE) +int fetchfile_absolute (lua_State *L) +{ + char path[MAXPATHLEN]; + switch (nse_fetchfile_absolute(path, sizeof(path), luaL_checkstring(L, 1))) { - error("%s: No files in '%s\\*'", SCRIPT_ENGINE, dirname); - return 0; + case 0: // no such path + lua_pushnil(L); + lua_pushfstring(L, "no path to file/directory: %s", lua_tostring(L, 1)); + break; + case 1: // file returned + lua_pushliteral(L, "file"); + lua_pushstring(L, path); + break; + case 2: // directory returned + lua_pushliteral(L, "directory"); + lua_pushstring(L, path); + break; + default: + return luaL_error(L, "nse_fetchfile_absolute returned bad code"); } - - while(!(morefiles == FALSE && GetLastError() == ERROR_NO_MORE_FILES)) { - // if we are looking for files and this file doesn't end with .nse or - // is a directory, then we don't look further at it - if(files_or_dirs == NSE_FILES) { - if(!((nse_check_extension(SCRIPT_ENGINE_EXTENSION, entry.cFileName)) - && !(entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - )) { - morefiles = FindNextFile(dir, &entry); - continue; - } - - // if we are looking for dirs and this dir - // isn't a directory, then we don't look further at it - } else if(files_or_dirs == NSE_DIRS) { - if(!(entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { - morefiles = FindNextFile(dir, &entry); - continue; - } - - // they have passed an invalid value for files_or_dirs - } else { - fatal("%s: In: %s:%i This should never happen.", - SCRIPT_ENGINE, __FILE__, __LINE__); - } - - // otherwise we add it to the results - // we assume that dirname ends with a directory separator of some kind - path = std::string(dirname) + "\\" + std::string(entry.cFileName); - lua_pushstring(L, path.c_str()); - lua_rawseti(L, -2, lua_objlen(L, -2) + 1); - morefiles = FindNextFile(dir, &entry); - } - - - return 1; + return 2; } + +/* LuaFileSystem directory iterator port. + * + * LuaFileSystem library: + * by Roberto Ierusalimschy, Andre Carregal and Tomas Guisasola + * as part of the Kepler Project. + * LuaFileSystem is currently maintained by Fabio Mascarenhas. + * + * LuaFileSystem is a Lua library developed to complement the set + * of functions related to file systems offered by the standard + * Lua distribution. + * LuaFileSystem offers a portable way to access the underlying + * directory structure and file attributes. + * + * LuaFileSystem is free software and uses the same license as Lua 5.1. + * + * the most recent copy can be found at + * http://www.keplerproject.org/luafilesystem/ + * + * Note: this is a port of the LuaFileSystem directory iterator for the + * Nmap project http://nmap.org + **/ + +/* +** Directory iterator +*/ +static int dir_iter (lua_State *L) { +#ifdef WIN32 + struct _finddata_t c_file; #else - -int nse_scandir (lua_State *L) { - DIR* dir; - struct dirent* entry; - struct stat stat_entry; - const char *dirname = luaL_checkstring(L, 1); - int files_or_dirs = luaL_checkint(L, 2); - - lua_createtable(L, 100, 0); // 100 files average - - dir = opendir(dirname); - if(dir == NULL) { - error("%s: Could not open directory '%s'.", SCRIPT_ENGINE, dirname); + struct dirent *entry; +#endif + dir_data *d = (dir_data *)luaL_checkudata(L, 1, DIR_METATABLE); + luaL_argcheck(L, !d->closed, 1, "closed directory"); +#ifdef WIN32 + if (d->hFile == 0L) { /* first entry */ + if ((d->hFile = _findfirst(d->pattern, &c_file)) == -1L) { + lua_pushnil(L); + lua_pushstring(L, strerror (errno)); + return 2; + } else { + lua_pushstring(L, c_file.name); + return 1; + } + } else { /* next entry */ + if (_findnext(d->hFile, &c_file) == -1L) { + /* no more entries => close directory */ + _findclose(d->hFile); + d->closed = 1; + return 0; + } else { + lua_pushstring(L, c_file.name); + return 1; + } + } +#else + if ((entry = readdir(d->dir)) != NULL) { + lua_pushstring(L, entry->d_name); + return 1; + } else { + /* no more entries => close directory */ + closedir(d->dir); + d->closed = 1; return 0; } - - // note that if there is a symlink in the dir, we have to rely on - // the .nse extension - // if they provide a symlink to a dir which ends with .nse, things - // break :/ - while((entry = readdir(dir)) != NULL) { - std::string path = std::string(dirname) + "/" + std::string(entry->d_name); - - if(stat(path.c_str(), &stat_entry) != 0) - fatal("%s: In: %s:%i This should never happen.", - SCRIPT_ENGINE, __FILE__, __LINE__); - - // if we are looking for files and this file doesn't end with .nse and - // isn't a file or a link, then we don't look further at it - if(files_or_dirs == NSE_FILES) { - if(!(nse_check_extension(SCRIPT_ENGINE_EXTENSION, entry->d_name) - && (S_ISREG(stat_entry.st_mode) - || S_ISLNK(stat_entry.st_mode)) - )) { - continue; - } - - // if we are looking for dirs and this dir - // isn't a dir or a link, then we don't look further at it - } else if(files_or_dirs == NSE_DIRS) { - if(!(S_ISDIR(stat_entry.st_mode) - || S_ISLNK(stat_entry.st_mode) - )) { - continue; - } - - // they have passed an invalid value for files_or_dirs - } else { - fatal("%s: In: %s:%i This should never happen.", - SCRIPT_ENGINE, __FILE__, __LINE__); - } - - // otherwise we add it to the results - lua_pushstring(L, path.c_str()); - lua_rawseti(L, -2, lua_objlen(L, -2) + 1); - } - - closedir(dir); - - return 1; +#endif } +/* +** Closes directory iterators +*/ +static int dir_close (lua_State *L) { + dir_data *d = (dir_data *)lua_touserdata(L, 1); +#ifdef WIN32 + if (!d->closed && d->hFile) { + _findclose(d->hFile); + d->closed = 1; + } +#else + if (!d->closed && d->dir) { + closedir(d->dir); + d->closed = 1; + } #endif + return 0; +} + +/* +** Factory of directory iterators +*/ +int nse_readdir (lua_State *L) { + const char *dirname = luaL_checkstring(L, 1); + dir_data *d; + lua_pushcfunction(L, dir_iter); + d = (dir_data *)lua_newuserdata(L, sizeof(dir_data)); + d->closed = 0; +#ifdef WIN32 + d->hFile = 0L; + luaL_getmetatable(L, DIR_METATABLE); + lua_setmetatable(L, -2); + if (strlen(dirname) > MAX_DIR_LENGTH) + luaL_error(L, "%s: Path too long '%s'.", SCRIPT_ENGINE, dirname); + else + Snprintf(d->pattern, MAX_DIR_LENGTH, "%s/*", dirname); +#else + luaL_getmetatable(L, DIR_METATABLE); + lua_setmetatable(L, -2); + d->dir = opendir(dirname); + if (d->dir == NULL) + luaL_error(L, "%s: Could not open directory '%s'.", SCRIPT_ENGINE, dirname); +#endif + return 2; +} + +int luaopen_fs(lua_State *L) +{ + /* create the dir metatable */ + luaL_newmetatable(L, DIR_METATABLE); + lua_pushstring(L, "__index"); + lua_newtable(L); + lua_pushstring(L, "next"); + lua_pushcfunction(L, dir_iter); + lua_settable(L, -3); + lua_pushstring (L, "close"); + lua_pushcfunction (L, dir_close); + lua_settable(L, -3); + lua_settable(L, -3); + lua_pushstring(L, "__gc"); + lua_pushcfunction (L, dir_close); + lua_settable(L, -3); + lua_pop(L, 1); + return 0; +} diff --git a/nse_fs.h b/nse_fs.h index e5fe55b4b..2be2486e7 100644 --- a/nse_fs.h +++ b/nse_fs.h @@ -1,15 +1,10 @@ #ifndef NSE_FS #define NSE_FS -int nse_check_extension (const char* ext, const char* path); +int fetchfile_absolute (lua_State *L); -int nse_fetchfile(char *path, size_t path_len, const char *file); +int nse_readdir (lua_State *L); -int nse_fetchfile_absolute(char *path, size_t path_len, const char *file); - -int nse_scandir (lua_State *L); - -#define NSE_FILES 1 -#define NSE_DIRS 2 +int luaopen_fs (lua_State *L); #endif diff --git a/nse_main.cc b/nse_main.cc index b96addb4c..7175abf83 100644 --- a/nse_main.cc +++ b/nse_main.cc @@ -31,7 +31,9 @@ #define NSE_SELECTED_BY_NAME "NSE_SELECTED_BY_NAME" #define NSE_CURRENT_HOSTS "NSE_CURRENT_HOSTS" -#define MAX_FILENAME_LEN 4096 +#ifndef MAXPATHLEN +# define MAXPATHLEN 2048 +#endif extern NmapOps o; @@ -123,39 +125,6 @@ static int port_set_output (lua_State *L) return 0; } -static int fetchfile_absolute (lua_State *L) -{ - char path[MAX_FILENAME_LEN]; - switch (nse_fetchfile_absolute(path, sizeof(path), luaL_checkstring(L, 1))) - { - case 0: // no such path - lua_pushnil(L); - lua_pushfstring(L, "no path to file/directory: %s", lua_tostring(L, 1)); - break; - case 1: // file returned - lua_pushliteral(L, "file"); - lua_pushstring(L, path); - break; - case 2: // directory returned - lua_pushliteral(L, "directory"); - lua_pushstring(L, path); - break; - default: - return luaL_error(L, "nse_fetchfile_absolute returned bad code"); - } - return 2; -} - -static int dump_dir (lua_State *L) -{ - luaL_checkstring(L, 1); - lua_pushcclosure(L, nse_scandir, 0); - lua_pushvalue(L, 1); - lua_pushinteger(L, NSE_FILES); - lua_call(L, 2, 1); - return 1; -} - /* This must call the l_nsock_loop function defined in nse_nsock.cc. * That closure is created in luaopen_nsock in order to allow * l_nsock_loop to have access to the nsock library environment. @@ -217,7 +186,7 @@ static void open_cnse (lua_State *L) { static const luaL_Reg nse[] = { {"fetchfile_absolute", fetchfile_absolute}, - {"dump_dir", dump_dir}, + {"dir", nse_readdir}, {"nsock_loop", nsock_loop}, {"key_was_pressed", key_was_pressed}, {"updatedb", updatedb}, @@ -231,6 +200,9 @@ static void open_cnse (lua_State *L) {NULL, NULL} }; + /* create dir metatable */ + luaopen_fs(L); + lua_newtable(L); luaL_register(L, NULL, nse); /* Add some other fields */ @@ -325,7 +297,13 @@ int script_updatedb (void) "local db = assert(open(path..'script.db', 'w'),\n" " 'could not open database for writing')\n" /* dump the scripts/categories */ - "local scripts = nse.dump_dir(path)\n" + "local scripts = {}\n" + "for f in nse.dir(path) do\n" + " if match(f, '%.nse$') then\n" + " local file = path ..\"/\".. f\n" + " table.insert(scripts, file)\n" + " end\n" + "end\n" "table.sort(scripts)\n" "for i, script in ipairs(scripts) do\n" " local env = setmetatable({}, {__index = _G})\n" @@ -377,7 +355,7 @@ int script_updatedb (void) static int init_main (lua_State *L) { - char path[MAX_FILENAME_LEN]; + char path[MAXPATHLEN]; std::vector *rules = (std::vector *) lua_touserdata(L, 1); @@ -397,7 +375,7 @@ static int init_main (lua_State *L) lua_setfield(L, LUA_REGISTRYINDEX, NSE_TRACEBACK); /* save copy */ /* Load main Lua code, stack position 2 */ - if (nmap_fetchfile(path, MAX_FILENAME_LEN, "nse_main.lua") != 1) + if (nmap_fetchfile(path, sizeof(path), "nse_main.lua") != 1) luaL_error(L, "could not locate nse_main.lua"); if (luaL_loadfile(L, path) != 0) luaL_error(L, "could not load nse_main.lua: %s", lua_tostring(L, -1)); diff --git a/nse_main.lua b/nse_main.lua index 65ce55233..ab14db172 100644 --- a/nse_main.lua +++ b/nse_main.lua @@ -469,8 +469,9 @@ local function get_chosen_scripts (rules) print_debug(2, "Script %s was selected by name.", script.filename); files_loaded[path] = true; elseif t == "directory" then - for i, file in ipairs(cnse.dump_dir(path)) do - if not files_loaded[file] then + for f in cnse.dir(path) do + local file = path .."/".. f + if find(f, "%.nse$") and not files_loaded[file] then chosen_scripts[#chosen_scripts+1] = Script.new(file); files_loaded[file] = true; end diff --git a/nse_nsock.cc b/nse_nsock.cc index 548e5ca5f..2bd6f3d51 100644 --- a/nse_nsock.cc +++ b/nse_nsock.cc @@ -26,7 +26,10 @@ extern "C" # include #endif -#define SCRIPT_ENGINE "NSE" +#ifndef SCRIPT_ENGINE +# define SCRIPT_ENGINE "NSE" +#endif + #define NSOCK_WRAPPER "NSOCK WRAPPER" #define NSOCK_WRAPPER_SUCCESS 0 #define NSOCK_WRAPPER_ERROR 2