From d81ead72dc9cf45bf244f430abcc678bf40a6e3e Mon Sep 17 00:00:00 2001 From: dmiller Date: Thu, 29 Sep 2022 22:19:28 +0000 Subject: [PATCH] New C backend for datafiles.lua; avoid copies of large data sets --- Makefile.in | 6 +-- mswin32/nmap.vcxproj | 2 + nse_db.cc | 113 +++++++++++++++++++++++++++++++++++++++++++ nse_db.h | 7 +++ nse_main.cc | 2 + nselib/datafiles.lua | 70 ++++++++++++++++----------- 6 files changed, 169 insertions(+), 31 deletions(-) create mode 100644 nse_db.cc create mode 100644 nse_db.h diff --git a/Makefile.in b/Makefile.in index ccfceda29..7a33bf45b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -88,9 +88,9 @@ UNINSTALLNDIFF=@UNINSTALLNDIFF@ UNINSTALLNPING=@UNINSTALLNPING@ ifneq (@NOLUA@,yes) -NSE_SRC=nse_main.cc nse_utility.cc nse_nsock.cc nse_dnet.cc nse_fs.cc nse_nmaplib.cc nse_debug.cc nse_pcrelib.cc nse_lpeg.cc -NSE_HDRS=nse_main.h nse_utility.h nse_nsock.h nse_dnet.h nse_fs.h nse_nmaplib.h nse_debug.h nse_pcrelib.h nse_lpeg.h -NSE_OBJS=nse_main.o nse_utility.o nse_nsock.o nse_dnet.o nse_fs.o nse_nmaplib.o nse_debug.o nse_pcrelib.o nse_lpeg.o +NSE_SRC=nse_main.cc nse_utility.cc nse_nsock.cc nse_db.cc nse_dnet.cc nse_fs.cc nse_nmaplib.cc nse_debug.cc nse_pcrelib.cc nse_lpeg.cc +NSE_HDRS=nse_main.h nse_utility.h nse_nsock.h nse_db.h nse_dnet.h nse_fs.h nse_nmaplib.h nse_debug.h nse_pcrelib.h nse_lpeg.h +NSE_OBJS=nse_main.o nse_utility.o nse_nsock.o nse_db.o nse_dnet.o nse_fs.o nse_nmaplib.o nse_debug.o nse_pcrelib.o nse_lpeg.o ifneq (@OPENSSL_LIBS@,) NSE_SRC+=nse_openssl.cc nse_ssl_cert.cc NSE_HDRS+=nse_openssl.h nse_ssl_cert.h diff --git a/mswin32/nmap.vcxproj b/mswin32/nmap.vcxproj index 2a9c3630a..d1362bacf 100644 --- a/mswin32/nmap.vcxproj +++ b/mswin32/nmap.vcxproj @@ -204,6 +204,7 @@ + @@ -263,6 +264,7 @@ + diff --git a/nse_db.cc b/nse_db.cc new file mode 100644 index 000000000..e268d59db --- /dev/null +++ b/nse_db.cc @@ -0,0 +1,113 @@ +#include + +#include "nse_lua.h" +#include "MACLookup.h" +#include "services.h" +#include "protocols.h" + +static inline u8 nibble(char hex) { + return (hex & 0xf) + ((hex & 0x40) ? 9 : 0); +} + +static int l_mac2corp (lua_State *L) +{ + size_t len = 0; + u8 prefix[6] = {0}; // allow a whole MAC addr. + size_t i = 0; + size_t j = 0; + const char *buf = luaL_checklstring(L, 1, &len); + + if (len == 6) { + // Option 1: 6-byte raw MAC + lua_pushstring(L, MACPrefix2Corp((u8 *)buf)); + return 1; + } + + // Try for hex string. + for (i = 0; i + 1 < len && j < 6; i+=2 ) { + if (buf[i] == ':' && i + 2 < len) { + i++; + } + if (isxdigit(buf[i]) && isxdigit(buf[i+1])) { + prefix[j++] = (nibble(buf[i]) << 4) + nibble(buf[i+1]); + } + else { + break; + } + } + // Require exactly 6 bytes result and used the whole input + if (j == 6 && i >= len) { + lua_pushstring(L, MACPrefix2Corp(prefix)); + return 1; + } + return luaL_error(L, "Expected a 6-byte MAC address"); +} + +static int l_getservbyport (lua_State *L) +{ + const struct nservent *serv = NULL; + static const u16 proto[] = {IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP}; + static const char * op[] = {"tcp", "udp", "sctp"}; + lua_Integer port = luaL_checkinteger(L, 1); + int i = luaL_checkoption(L, 2, NULL, op); + + if (port < 0 || port > 0xffff) { + return luaL_error(L, "Port number out of range"); + } + + serv = nmap_getservbyport((u16) port, proto[i]); + if (serv == NULL) { + lua_pushnil(L); + } + else { + lua_pushstring(L, serv->s_name); + } + return 1; +} + +static int l_getprotbynum (lua_State *L) +{ + const struct nprotoent *proto = NULL; + lua_Integer num = luaL_checkinteger(L, 1); + + if (num < 0 || num > 0xff) { + return luaL_error(L, "Protocol number out of range"); + } + + proto = nmap_getprotbynum(num); + if (proto == NULL) { + lua_pushnil(L); + } + else { + lua_pushstring(L, proto->p_name); + } + return 1; +} + +static int l_getprotbyname (lua_State *L) +{ + const struct nprotoent *proto = NULL; + const char *name = luaL_checkstring(L, 1); + + proto = nmap_getprotbyname(name); + if (proto == NULL) { + lua_pushnil(L); + } + else { + lua_pushinteger(L, proto->p_proto); + } + return 1; +} + +int luaopen_db (lua_State *L) +{ + static const luaL_Reg dblib [] = { + {"mac2corp", l_mac2corp}, + {"getservbyport", l_getservbyport}, + {"getprotbynum", l_getprotbynum}, + {"getprotbyname", l_getprotbyname}, + {NULL, NULL} + }; + luaL_newlib(L, dblib); + return 1; +} diff --git a/nse_db.h b/nse_db.h new file mode 100644 index 000000000..d80239bc2 --- /dev/null +++ b/nse_db.h @@ -0,0 +1,7 @@ +#ifndef NSE_DB +#define NSE_DB + +#define NSE_DBLIBNAME "nmapdb" +LUALIB_API int luaopen_db (lua_State *L); + +#endif diff --git a/nse_main.cc b/nse_main.cc index 110291f57..152bece2a 100644 --- a/nse_main.cc +++ b/nse_main.cc @@ -11,6 +11,7 @@ #include "nse_main.h" #include "nse_utility.h" +#include "nse_db.h" #include "nse_fs.h" #include "nse_nsock.h" #include "nse_nmaplib.h" @@ -559,6 +560,7 @@ static void set_nmap_libraries (lua_State *L) static const luaL_Reg libs[] = { {NSE_PCRELIBNAME, luaopen_pcrelib}, {NSE_NMAPLIBNAME, luaopen_nmap}, + {NSE_DBLIBNAME, luaopen_db}, {LFSLIBNAME, luaopen_lfs}, {LPEGLIBNAME, luaopen_lpeg}, #ifdef HAVE_LIBSSH2 diff --git a/nselib/datafiles.lua b/nselib/datafiles.lua index 2e3bad908..ffe71f193 100644 --- a/nselib/datafiles.lua +++ b/nselib/datafiles.lua @@ -15,6 +15,8 @@ local nmap = require "nmap" local stdnse = require "stdnse" local string = require "string" local table = require "table" +-- mostly undocumented library for direct lookups in Nmap datafiles: +local nmapdb = require "nmapdb" _ENV = stdnse.module("datafiles", stdnse.seeall) @@ -72,59 +74,71 @@ parse_rpc = function() return parse_and_cache("nmap-rpc") end +local prohibited = function() + error("Invalid function") +end +local services_table = {} +local portlookup_mt = { + __index = function(t, port) + return nmapdb.getservbyport(port, rawget(t, "proto")) + end, + __newindex = prohibited, +} +for _, proto in ipairs({"tcp", "udp", "sctp"}) do + services_table[proto] = setmetatable({proto=proto}, portlookup_mt) +end --- -- Read and parse nmap-services. -- --- On success, return true and a table containing two subtables, indexed by the --- keys "tcp" and "udp". The tcp subtable maps TCP port numbers to --- service names, and the udp subtable is the same for UDP. You can --- pass "tcp" or "udp" as an argument to parse_services to get +-- On success, return true and a table containing subtables indexed by the +-- keys "tcp", "udp", and "sctp". You can +-- pass a protocol name as an argument to parse_services to get -- only one of the results tables. --- @param protocol The protocol table to return ("tcp" or +-- @param protocol Optional: The protocol table to return (e.g. "tcp" or -- "udp"). -- @return Status (true or false). -- @return Table (if status is true) or error string (if status is false). -- @see parse_file parse_services = function(protocol) - if protocol and protocol ~= "tcp" and protocol ~= "udp" then - return false, "Bad protocol for nmap-services: use tcp or udp" - end - - local services_table - nmap.registry.datafiles = nmap.registry.datafiles or {} - nmap.registry.datafiles.services = nmap.registry.datafiles.services or {} + local t if protocol then - if not nmap.registry.datafiles.services[protocol] then - local status - status, nmap.registry.datafiles.services[protocol] = parse_file("nmap-services", protocol) - if not status then - return false, "Error parsing nmap-services" - end + t = services_table[protocol] + if not t then + return false, "Bad protocol for nmap-services" end - services_table = nmap.registry.datafiles.services[protocol] else - local status - status, nmap.registry.datafiles.services = parse_file("nmap-services") - if not status then - return false, "Error parsing nmap-services" - end - services_table = nmap.registry.datafiles.services + t = services_table end - return true, services_table + return true, t end +local mac_table = setmetatable({}, { + __index = function(t, mac) + if #mac < 6 then + -- probably binary + mac = mac .. ("\0"):rep(6 - #mac) + elseif #mac < 12 then + -- probably hex + mac = mac .. ("0"):rep(12 - #mac) + end + return nmapdb.mac2corp(mac) + end, + __newindex = prohibited, +}) --- -- Read and parse nmap-mac-prefixes. -- --- On success, return true and a table mapping 3 byte MAC prefixes to manufacturer names. +-- On success, return true and a table mapping MAC prefixes to manufacturer +-- names. The whole MAC can also be used as a key, since the table calls an +-- internal Nmap function to do the lookup. -- @return Status (true or false). -- @return Table (if status is true) or error string (if status is false). -- @see parse_file parse_mac_prefixes = function() - return parse_and_cache("nmap-mac-prefixes") + return true, mac_table end