From de4ba536deb56ecddc1d52be9e4b06aad91e29f3 Mon Sep 17 00:00:00 2001 From: batrick Date: Sat, 18 Sep 2010 20:35:09 +0000 Subject: [PATCH] Merge from /nmap-exp/patrick/nse-nsock-maintenance. This is a maintenance fix for the NSE Nsock library binding. The patch focuses on code correctness and simplicity. The patch also brings some initial updates with an eye towards the upcoming Lua 5.2 release. See [1] for a post concerning this branch. [1] http://seclists.org/nmap-dev/2010/q3/710 --- Makefile.in | 6 +- mswin32/nmap.vcproj | 16 + nse_dnet.cc | 313 +++++ nse_dnet.h | 8 + nse_main.cc | 51 +- nse_main.h | 4 +- nse_nmaplib.cc | 109 +- nse_nsock.cc | 2736 ++++++++++-------------------------- nse_nsock.h | 9 +- nse_utility.cc | 180 +++ nse_utility.h | 19 + nselib/dhcp.lua | 16 +- nselib/nmap.luadoc | 36 +- nselib/ssh1.lua | 11 +- nselib/ssh2.lua | 9 +- scripts/firewalk.nse | 25 +- scripts/ipidseq.nse | 16 +- scripts/path-mtu.nse | 14 +- scripts/qscan.nse | 14 +- scripts/sniffer-detect.nse | 21 +- 20 files changed, 1446 insertions(+), 2167 deletions(-) create mode 100644 nse_dnet.cc create mode 100644 nse_dnet.h create mode 100644 nse_utility.cc create mode 100644 nse_utility.h diff --git a/Makefile.in b/Makefile.in index 7037bbad0..c60a427b7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -82,9 +82,9 @@ UNINSTALLZENMAP=@UNINSTALLZENMAP@ UNINSTALLNPING=@UNINSTALLNPING@ ifneq (@LIBLUA_LIBS@,) -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_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 +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_binlib.cc nse_bit.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_binlib.h nse_bit.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_binlib.o nse_bit.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.vcproj b/mswin32/nmap.vcproj index cff117de3..b162ba511 100644 --- a/mswin32/nmap.vcproj +++ b/mswin32/nmap.vcproj @@ -278,6 +278,10 @@ RelativePath="..\nse_main.cc" > + + @@ -286,6 +290,10 @@ RelativePath="..\nse_nsock.cc" > + + @@ -479,6 +487,10 @@ RelativePath="..\nse_main.h" > + + @@ -487,6 +499,10 @@ RelativePath="..\nse_nsock.h" > + + diff --git a/nse_dnet.cc b/nse_dnet.cc new file mode 100644 index 000000000..b39a12bc0 --- /dev/null +++ b/nse_dnet.cc @@ -0,0 +1,313 @@ +#include "nsock.h" +#include "nmap_error.h" +#include "NmapOps.h" +#include "utils.h" +#include "tcpip.h" +#include "protocols.h" +#include "libnetutil/netutil.h" + +#include "nse_main.h" +#include "nse_utility.h" + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +} + +#include + +extern NmapOps o; + +/* Map of dnet userdata to ethernet device userdata */ +#define ETH_CACHE_DNET_ETH 0 +/* Map of ethernet device string identifier to ethernet device userdata */ +#define ETH_CACHE_DEVICE_ETH 1 + +/* metatable entries in the registry */ +#define DNET_METATABLE "DNET_METATABLE" +#define DNET_ETH_METATABLE "DNET_ETH_METATABLE" + +typedef struct nse_dnet_udata +{ + eth_t *eth; + int sock; /* raw ip socket */ + int interface; /* integer reference in registry for interface string */ +} nse_dnet_udata; + +LUALIB_API int l_dnet_new (lua_State *L) +{ + nse_dnet_udata *udata; + + udata = (nse_dnet_udata *) lua_newuserdata(L, sizeof(nse_dnet_udata)); + luaL_getmetatable(L, DNET_METATABLE); + lua_setmetatable(L, -2); + udata->eth = NULL; + udata->sock = -1; + udata->interface = LUA_NOREF; + + return 1; +} + +LUALIB_API int l_dnet_get_interface_link (lua_State *L) +{ + struct interface_info *ii = getInterfaceByName(luaL_checkstring(L, 1)); + + if (ii == NULL) + return luaL_argerror(L, 1, "bad interface"); + + switch (ii->device_type) + { + case devt_ethernet: + lua_pushliteral(L, "ethernet"); + return 1; + case devt_loopback: + lua_pushliteral(L, "loopback"); + return 1; + case devt_p2p: + lua_pushliteral(L, "p2p"); + return 1; + case devt_other: + default: + lua_pushnil(L); + return 1; + } +} + +static int close_eth (lua_State *L) +{ + eth_t **eth = (eth_t **) luaL_checkudata(L, 1, DNET_ETH_METATABLE); + assert(*eth != NULL); + eth_close(*eth); + *eth = NULL; + return success(L); +} + +static eth_t *open_eth_cached (lua_State *L, int dnet_index, const char *device) +{ + eth_t **eth; + + lua_rawgeti(L, LUA_ENVIRONINDEX, ETH_CACHE_DNET_ETH); + lua_rawgeti(L, LUA_ENVIRONINDEX, ETH_CACHE_DEVICE_ETH); + lua_getfield(L, -1, device); + if (!lua_isuserdata(L, -1)) + { + eth = (eth_t **) lua_newuserdata(L, sizeof(eth_t *)); + *eth = eth_open(device); + if (*eth == NULL) + luaL_error(L, "unable to open dnet on ethernet interface %s", device); + luaL_getmetatable(L, DNET_ETH_METATABLE); + lua_setmetatable(L, -2); + lua_pushvalue(L, -1); + lua_setfield(L, -4, device); + lua_replace(L, -2); /* replace nil */ + } + eth = (eth_t **) lua_touserdata(L, -1); + + lua_pushvalue(L, dnet_index); + lua_pushvalue(L, -2); /* eth_t userdata */ + lua_rawset(L, -5); /* add to ETH_CACHE_DNET_ETH */ + lua_pop(L, 3); /* ETH_CACHE_DNET_ETH, ETH_CACHE_DEVICE_ETH, eth_t userdata */ + + return *eth; +} + +static int ethernet_open (lua_State *L) +{ + nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE); + const char *interface_name = luaL_checkstring(L, 2); + struct interface_info *ii = getInterfaceByName(interface_name); + + if (ii == NULL || ii->device_type != devt_ethernet) + return luaL_argerror(L, 2, "device is not valid ethernet interface"); + + udata->eth = open_eth_cached(L, 1, interface_name); + lua_pushvalue(L, 2); + udata->interface = luaL_ref(L, LUA_REGISTRYINDEX); + + return success(L); +} + +static int ethernet_close (lua_State *L) +{ + nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE); + + udata->eth = NULL; + + lua_rawgeti(L, LUA_ENVIRONINDEX, ETH_CACHE_DNET_ETH); + lua_pushvalue(L, 1); + lua_pushnil(L); + lua_rawset(L, -3); + + luaL_unref(L, LUA_REGISTRYINDEX, udata->interface); + udata->interface = LUA_NOREF; + + return success(L); +} + +static int ethernet_send (lua_State *L) +{ + nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE); + if (udata->eth == NULL) + return luaL_error(L, "dnet ethernet interface is not open"); + eth_send(udata->eth, luaL_checkstring(L, 2), lua_objlen(L, 2)); + return success(L); +} + +static int ip_open (lua_State *L) +{ + nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE); + udata->sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (udata->sock == -1) + return luaL_error(L, "failed to open raw socket: %s (errno %d)", + socket_strerror(socket_errno()), socket_errno()); + broadcast_socket(udata->sock); +#ifndef WIN32 + sethdrinclude(udata->sock); +#endif + return success(L); +} + +static int ip_close (lua_State *L) +{ + nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE); + if (udata->sock == -1) + return safe_error(L, "raw socket already closed"); + close(udata->sock); + udata->sock = -1; + return success(L); +} + +static int ip_send (lua_State *L) +{ + nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE); + const char *packet = luaL_checkstring(L, 2); + + int ret; + + if (udata->sock == -1) + return luaL_error(L, "raw socket not open to send"); + + if (lua_objlen(L, 2) < sizeof(struct ip)) + return luaL_error(L, "ip packet too short"); + + if (o.sendpref & PACKET_SEND_ETH) + { + struct route_nfo route; + struct sockaddr_storage srcss, dstss, *nexthop; + struct sockaddr_in *srcsin = (struct sockaddr_in *) &srcss; + struct sockaddr_in *dstsin = (struct sockaddr_in *) &dstss; + struct ip *ip = (struct ip *) packet; + u8 dstmac[6]; + eth_nfo eth; + + /* build sockaddr for target from user packet and determine route */ + memset(&dstss, 0, sizeof(dstss)); + dstsin->sin_family = AF_INET; + dstsin->sin_addr.s_addr = ip->ip_dst.s_addr; + + if (!nmap_route_dst(&dstss, &route)) + goto usesock; + + if (route.ii.device_type != devt_ethernet) + goto usesock; + + /* above we fallback to using the raw socket if we can't find an (ethernet) + * route to the host. From here on out it's ethernet all the way. + */ + + /* build sockaddr for source from user packet to determine next hop mac */ + memset(&srcss, 0, sizeof(srcss)); + srcsin->sin_family = AF_INET; + srcsin->sin_addr.s_addr = ip->ip_src.s_addr; + + if (route.direct_connect) + nexthop = &dstss; + else + nexthop = &route.nexthop; + + if (!getNextHopMAC(route.ii.devfullname, route.ii.mac, &srcss, nexthop, dstmac)) + return luaL_error(L, "failed to determine next hop MAC address"); + + /* Use cached ethernet device, and use udata's eth and interface to keep + * track of if we're reusing the same device from the previous packet, and + * close the cached device if not. + */ + memset(ð, 0, sizeof(eth)); + memcpy(eth.srcmac, route.ii.mac, sizeof(eth.srcmac)); + memcpy(eth.dstmac, dstmac, sizeof(eth.dstmac)); + + /* close any current ethernet associated with this userdata */ + lua_pushcfunction(L, ethernet_close); + lua_pushvalue(L, 1); + lua_call(L, 1, 0); + + udata->eth = eth.ethsd = open_eth_cached(L, 1, route.ii.devname); + lua_pushstring(L, route.ii.devname); + udata->interface = luaL_ref(L, LUA_REGISTRYINDEX); + + ret = send_ip_packet(udata->sock, ð, (u8 *) packet, lua_objlen(L, 2)); + } else { +usesock: +#ifdef WIN32 + if (strlen(dev) > 0) + win32_warn_raw_sockets(dev); +#endif + ret = send_ip_packet(udata->sock, NULL, (u8 *) packet, lua_objlen(L, 2)); + } + if (ret == -1) + return luaL_error(L, "error while sending: %s (errno %d)", + socket_strerror(socket_errno()), socket_errno()); + + return success(L); +} + +static int gc (lua_State *L) +{ + nse_dnet_udata *udata = (nse_dnet_udata *) luaL_checkudata(L, 1, DNET_METATABLE); + + lua_pushcfunction(L, ip_close); + lua_pushvalue(L, 1); + lua_call(L, 1, 0); + lua_pushcfunction(L, ethernet_close); + lua_pushvalue(L, 1); + lua_call(L, 1, 0); + + luaL_unref(L, LUA_REGISTRYINDEX, udata->interface); + return 0; +} + +LUALIB_API int luaopen_dnet (lua_State *L) +{ + static const luaL_reg l_dnet[] = { + {"ethernet_open", ethernet_open}, + {"ethernet_close", ethernet_close}, + {"ethernet_send", ethernet_send}, + {"ip_open", ip_open}, + {"ip_close", ip_close}, + {"ip_send", ip_send}, + {NULL, NULL} + }; + + lua_createtable(L, 2, 0); + lua_replace(L, LUA_ENVIRONINDEX); + weak_table(L, 0, 0, "k"); /* dnet udata weak, eth device strong */ + lua_rawseti(L, LUA_ENVIRONINDEX, ETH_CACHE_DNET_ETH); + weak_table(L, 0, 0, "v"); /* eth_device weak */ + lua_rawseti(L, LUA_ENVIRONINDEX, ETH_CACHE_DEVICE_ETH); + + luaL_newmetatable(L, DNET_METATABLE); + lua_createtable(L, 0, 5); + luaL_register(L, NULL, l_dnet); + lua_setfield(L, -2, "__index"); + lua_newtable(L); + lua_setfield(L, -2, "__metatable"); + lua_pushcfunction(L, gc); + lua_setfield(L, -2, "__gc"); + + luaL_newmetatable(L, DNET_ETH_METATABLE); + lua_pushcfunction(L, close_eth); + lua_setfield(L, -2, "__gc"); + + return 0; +} diff --git a/nse_dnet.h b/nse_dnet.h new file mode 100644 index 000000000..296f8db5e --- /dev/null +++ b/nse_dnet.h @@ -0,0 +1,8 @@ +#ifndef NMAP_LUA_DNET_H +#define NMAP_LUA_DNET_H + +LUALIB_API int l_dnet_new (lua_State *); +LUALIB_API int l_dnet_get_interface_link (lua_State *); +LUALIB_API int luaopen_dnet (lua_State *L); + +#endif diff --git a/nse_main.cc b/nse_main.cc index 8f3c3398b..e280e654e 100644 --- a/nse_main.cc +++ b/nse_main.cc @@ -1,15 +1,3 @@ - -#include "nse_main.h" - -#include "nse_fs.h" -#include "nse_nsock.h" -#include "nse_nmaplib.h" -#include "nse_bit.h" -#include "nse_binlib.h" -#include "nse_pcrelib.h" -#include "nse_openssl.h" -#include "nse_debug.h" - #include "nmap.h" #include "nmap_error.h" #include "portlist.h" @@ -19,6 +7,17 @@ #include "Target.h" #include "nmap_tty.h" +#include "nse_main.h" +#include "nse_utility.h" +#include "nse_fs.h" +#include "nse_nsock.h" +#include "nse_nmaplib.h" +#include "nse_bit.h" +#include "nse_binlib.h" +#include "nse_pcrelib.h" +#include "nse_openssl.h" +#include "nse_debug.h" + #define NSE_MAIN "NSE_MAIN" /* the main function */ #define NSE_TRACEBACK "NSE_TRACEBACK" @@ -130,7 +129,8 @@ static int host_set_output (lua_State *L) static int port_set_output (lua_State *L) { - Port port, *p; + Port *p; + Port port; ScriptResult sr; Target *target = get_target(L, 1); p = get_port(L, target, &port, 2); @@ -406,6 +406,9 @@ static int run_main (lua_State *L) return 0; } +/* Lua 5.2 compatibility macro */ +#define lua_yieldk(L,n,ctx,k) lua_yield(L,n) + /* int nse_yield (lua_State *L) [-?, +?, e] * * This function will yield the running thread back to NSE, even across script @@ -413,12 +416,12 @@ static int run_main (lua_State *L) * correct and only way to call is as a tail call: * return nse_yield(L); */ -int nse_yield (lua_State *L) +int nse_yield (lua_State *L, int ctx, lua_CFunction k) { lua_getfield(L, LUA_REGISTRYINDEX, NSE_YIELD); lua_pushthread(L); lua_call(L, 1, 1); /* returns NSE_YIELD_VALUE */ - return lua_yield(L, 1); /* yield with NSE_YIELD_VALUE */ + return lua_yieldk(L, 1, ctx, k); /* yield with NSE_YIELD_VALUE */ } /* void nse_restore (lua_State *L, int number) [-, -, e] @@ -527,7 +530,14 @@ void open_nse (void) fatal("%s: failed to open a Lua state!", SCRIPT_ENGINE); lua_atpanic(L_NSE, panic); - if (lua_cpcall(L_NSE, init_main, (void *) &o.chosenScripts) != 0) +#if 0 + /* Lua 5.2 */ + lua_pushcfunction(L_NSE, init_main); + lua_pushlightuserdata(L_NSE, &o.chosenScripts); + if (lua_pcall(L_NSE, 1, 0, 0)) +#else + if (lua_cpcall(L_NSE, init_main, &o.chosenScripts)) +#endif fatal("%s: failed to initialize the script engine:\n%s\n", SCRIPT_ENGINE, lua_tostring(L_NSE, -1)); } @@ -540,7 +550,14 @@ void script_scan (std::vector &targets, stype scantype) assert(L_NSE != NULL); lua_settop(L_NSE, 0); /* clear the stack */ - if (lua_cpcall(L_NSE, run_main, (void *) &targets) != 0) +#if 0 + /* Lua 5.2 */ + lua_pushcfunction(L_NSE, run_main); + lua_pushlightuserdata(L_NSE, &targets); + if (lua_pcall(L_NSE, 1, 0, 0)) +#else + if (lua_cpcall(L_NSE, run_main, &targets)) +#endif error("%s: Script Engine Scan Aborted.\nAn error was thrown by the " "engine: %s", SCRIPT_ENGINE, lua_tostring(L_NSE, -1)); } diff --git a/nse_main.h b/nse_main.h index f74c13b0a..04541f694 100644 --- a/nse_main.h +++ b/nse_main.h @@ -9,8 +9,8 @@ extern "C" { #include "lua.h" - #include "lualib.h" #include "lauxlib.h" + #include "lualib.h" } #include "nmap.h" @@ -38,7 +38,7 @@ class Target; /* API */ -int nse_yield (lua_State *); +int nse_yield (lua_State *, int, lua_CFunction); void nse_restore (lua_State *, int); void nse_destructor (lua_State *, char); void nse_base (lua_State *); diff --git a/nse_nmaplib.cc b/nse_nmaplib.cc index bbfa96d55..34cb7c867 100644 --- a/nse_nmaplib.cc +++ b/nse_nmaplib.cc @@ -2,6 +2,7 @@ extern "C" { #include "lua.h" #include "lauxlib.h" + #include "lualib.h" } #include @@ -19,37 +20,15 @@ extern "C" { #include "protocols.h" #include "nse_nmaplib.h" +#include "nse_utility.h" #include "nse_nsock.h" +#include "nse_dnet.h" extern NmapOps o; static const char *NSE_PROTOCOL_OP[] = {"tcp", "udp", "sctp", NULL}; static const int NSE_PROTOCOL[] = {IPPROTO_TCP, IPPROTO_UDP, IPPROTO_SCTP}; -static void setsfield (lua_State *L, int idx, const char *field, const char *what) -{ - lua_pushvalue(L, idx); - lua_pushstring(L, what); /* what can be NULL */ - lua_setfield(L, -2, field); - lua_pop(L, 1); -} - -static void setnfield (lua_State *L, int idx, const char *field, lua_Number n) -{ - lua_pushvalue(L, idx); - lua_pushnumber(L, n); - lua_setfield(L, -2, field); - lua_pop(L, 1); -} - -static void setbfield (lua_State *L, int idx, const char *field, int b) -{ - lua_pushvalue(L, idx); - lua_pushboolean(L, b); - lua_setfield(L, -2, field); - lua_pop(L, 1); -} - void set_version (lua_State *L, const struct serviceDeductions *sd) { setsfield(L, -1, "name", sd->name); @@ -245,7 +224,7 @@ static int aux_mutex (lua_State *L) } lua_pushthread(L); lua_rawseti(L, lua_upvalueindex(1), lua_objlen(L, lua_upvalueindex(1))+1); - return nse_yield(L); + return nse_yield(L, 0, NULL); case DONE: lua_pushthread(L); if (!lua_equal(L, -1, lua_upvalueindex(2))) @@ -341,7 +320,7 @@ static int aux_condvar (lua_State *L) case WAIT: lua_pushthread(L); lua_rawseti(L, lua_upvalueindex(1), lua_objlen(L, lua_upvalueindex(1))+1); - return nse_yield(L); + return nse_yield(L, 0, NULL); case SIGNAL: n = lua_objlen(L, lua_upvalueindex(1)); break; @@ -398,56 +377,6 @@ static int l_condvar (lua_State *L) return 1; // condition variable closure } -Target *get_target (lua_State *L, int index) -{ - int top = lua_gettop(L); - Target *target; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "targetname"); - lua_getfield(L, index, "ip"); - if (!(lua_isstring(L, -2) || lua_isstring(L, -1))) - luaL_error(L, "host table does not have a 'ip' or 'targetname' field"); - if (lua_isstring(L, -2)) /* targetname */ - { - nse_gettarget(L, -2); /* use targetname */ - if (lua_islightuserdata(L, -1)) - goto done; - else - lua_pop(L, 1); - } - if (lua_isstring(L, -1)) /* ip */ - nse_gettarget(L, -1); /* use ip */ - if (!lua_islightuserdata(L, -1)) - luaL_argerror(L, 1, "host is not being processed right now"); -done: - target = (Target *) lua_touserdata(L, -1); - lua_settop(L, top); /* reset stack */ - return target; -} - -Port *get_port (lua_State *L, Target *target, Port *port, int index) -{ - Port *p = NULL; - int portno, protocol; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "number"); - if (!lua_isnumber(L, -1)) - luaL_error(L, "port 'number' field must be a number"); - lua_getfield(L, index, "protocol"); - if (!lua_isstring(L, -1)) - luaL_error(L, "port 'protocol' field must be a string"); - portno = (int) lua_tointeger(L, -2); - protocol = strcmp(lua_tostring(L, -1), "tcp") == 0 ? IPPROTO_TCP : - strcmp(lua_tostring(L, -1), "udp") == 0 ? IPPROTO_UDP : - strcmp(lua_tostring(L, -1), "sctp") == 0 ? IPPROTO_SCTP : - luaL_error(L, "port 'protocol' field must be \"udp\", \"sctp\" or \"tcp\""); - while ((p = target->ports.nextPort(p, port, protocol, PORT_UNKNOWN)) != NULL) - if (p->portno == portno) - break; - lua_pop(L, 2); - return p; -} - /* Generates an array of port data for the given host and leaves it on * the top of the stack */ @@ -457,7 +386,8 @@ static int l_get_ports (lua_State *L) "open|filtered", "closed|filtered", NULL}; static const int states[] = {PORT_OPEN, PORT_FILTERED, PORT_UNFILTERED, PORT_CLOSED, PORT_OPENFILTERED, PORT_CLOSEDFILTERED}; - Port *p = NULL, port; + Port *p = NULL; + Port port; /* dummy Port for nextPort */ Target *target = get_target(L, 1); int protocol = NSE_PROTOCOL[luaL_checkoption(L, 3, NULL, NSE_PROTOCOL_OP)]; int state = states[luaL_checkoption(L, 4, NULL, state_op)]; @@ -487,7 +417,8 @@ static int l_get_ports (lua_State *L) static int l_get_port_state (lua_State *L) { Target *target; - Port port, *p; + Port *p; + Port port; /* dummy Port */ target = get_target(L, 1); p = get_port(L, target, &port, 2); if (p == NULL) @@ -522,7 +453,8 @@ static int l_set_port_state (lua_State *L) static const int opstate[] = {PORT_OPEN, PORT_CLOSED}; static const char *op[] = {"open", "closed", NULL}; Target *target; - Port port, *p; + Port *p; + Port port; target = get_target(L, 1); if ((p = get_port(L, target, &port, 2)) != NULL) { @@ -561,7 +493,8 @@ static int l_set_port_version (lua_State *L) "incomplete" }; Target *target; - Port port, *p; + Port *p; + Port port; enum service_tunnel_type tunnel = SERVICE_TUNNEL_NONE; enum serviceprobestate probestate = opversion[luaL_checkoption(L, 3, "hardmatched", ops)]; @@ -839,19 +772,11 @@ int luaopen_nmap (lua_State *L) lua_settop(L, 0); // clear stack luaL_register(L, "nmap", nmaplib); - lua_newtable(L); - lua_createtable(L, 0, 1); - lua_pushliteral(L, "v"); - lua_setfield(L, -2, "__mode"); - lua_setmetatable(L, -2); // Allow closures to be collected (see l_mutex) + weak_table(L, 0, 0, "v"); /* allow closures to be collected (see l_mutex) */ lua_pushcclosure(L, l_mutex, 1); /* mutex function */ lua_setfield(L, -2, "mutex"); - lua_newtable(L); - lua_createtable(L, 0, 1); - lua_pushliteral(L, "v"); - lua_setfield(L, -2, "__mode"); - lua_setmetatable(L, -2); // Allow closures to be collected (see l_condvar) + weak_table(L, 0, 0, "v"); /* allow closures to be collected (see l_condvar) */ lua_pushcclosure(L, l_condvar, 1); // condvar function lua_setfield(L, -2, "condvar"); @@ -862,6 +787,10 @@ int luaopen_nmap (lua_State *L) lua_pushliteral(L, "nsock"); lua_call(L, 1, 0); + lua_pushcclosure(L, luaopen_dnet, 0); + lua_pushliteral(L, "dnet"); + lua_call(L, 1, 0); + lua_settop(L, 1); // just nmap lib on stack return 1; diff --git a/nse_nsock.cc b/nse_nsock.cc index 415b91c1e..8d58801b9 100644 --- a/nse_nsock.cc +++ b/nse_nsock.cc @@ -1,20 +1,3 @@ - -extern "C" -{ -#include "lua.h" -#include "lauxlib.h" -} - -#include -#include -#include -#include -#include - -#include "nse_nsock.h" -#include "nse_main.h" -#include "nse_ssl_cert.h" - #include "nsock.h" #include "nmap_error.h" #include "NmapOps.h" @@ -23,187 +6,100 @@ extern "C" #include "protocols.h" #include "libnetutil/netutil.h" +#include "nse_nsock.h" +#include "nse_main.h" +#include "nse_utility.h" +#include "nse_ssl_cert.h" + #if HAVE_OPENSSL /* See the comments in service_scan.cc for the reason for _WINSOCKAPI_. */ # define _WINSOCKAPI_ # include #endif -#define NSOCK_WRAPPER "NSOCK WRAPPER" -#define NSOCK_WRAPPER_SUCCESS 0 -#define NSOCK_WRAPPER_ERROR 2 +#include +#include +#include -#define NSOCK_WRAPPER_BUFFER_OK 1 -#define NSOCK_WRAPPER_BUFFER_MOREREAD 2 +#include +#include -#define FROM 1 -#define TO 2 +#define NMAP_NSOCK_SOCKET "NMAP_NSOCK_SOCKET" +#define NMAP_NSOCK_PCAP_SOCKET "NMAP_NSOCK_PCAP_SOCKET" #define DEFAULT_TIMEOUT 30000 +/* Integer keys in Nsock function environments */ +#define THREAD_SOCKETS 1 /* */ +#define CONNECT_WAITING 2 /* Threads waiting to lock */ +#define KEY_PCAP 3 /* keys to pcap sockets */ + +/* Integer keys in the Nsock userdata environments */ +#define THREAD_I 1 /* The thread that yielded */ +#define BUFFER_I 2 /* Location of Userdata Buffer */ + extern NmapOps o; -static int l_nsock_loop(lua_State * L); - -static int l_nsock_bind(lua_State * L); - -static int l_nsock_connect(lua_State * L); - -static int l_nsock_send(lua_State * L); - -static int l_nsock_receive(lua_State * L); - -static int l_nsock_receive_lines(lua_State * L); - -static int l_nsock_receive_bytes(lua_State * L); - -static int l_nsock_get_info(lua_State * L); - -static int l_nsock_gc(lua_State * L); - -static int l_nsock_close(lua_State * L); - -static int l_nsock_set_timeout(lua_State * L); - -static int l_nsock_receive_buf(lua_State * L); - -static int l_nsock_pcap_open(lua_State * L); - -static int l_nsock_pcap_close(lua_State * L); - -static int l_nsock_pcap_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 *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); - -int l_nsock_checkstatus(lua_State * L, nsock_event nse); - -void l_nsock_trace(nsock_iod nsiod, const char *message, int direction); - -static void l_dnet_open(lua_State * L); /* open dnet metatable */ - -const char *inet_ntop_both(int af, const void *v_addr, char *ipstring); - -unsigned short inet_port_both(int af, const void *v_addr); - -static nsock_pool nsp; - -/* - * Structure with nsock pcap descriptor. - * shared between many lua threads - */ -struct ncap_socket +typedef struct nse_nsock_udata { - nsock_iod nsiod; /* nsock pcap desc */ - int references; /* how many lua threads use - * this */ - char *key; /* (free) zero-terminated key - * used in map to * address - * this structure. */ -}; - -struct nsock_yield -{ - lua_State *thread; /* thread to resume */ - struct l_nsock_udata *udata; /* self reference */ -}; - -struct ncap_request -{ - int suspended; /* is the thread suspended? - * (lua_yield) */ - struct nsock_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 * address - * this structure (hexified - * 'test') */ - - bool received; /* are results ready? */ - - bool r_success; /* true-> okay,data ready to - * pass to user * flase-> this - * statusstring contains error - * description */ - char *r_status; /* errorstring */ - struct timeval recvtime; /* Time packet was received, - * if r_success is true */ - - unsigned char *r_layer2; - size_t r_layer2_len; - unsigned char *r_layer3; - size_t r_layer3_len; - size_t packetsz; - - int ncap_cback_ref; /* just copy of - * udata->ncap_cback_ref * - * because we don't have - * access to udata in place * - * we need to use this. */ -}; - -struct l_nsock_udata -{ - int timeout; nsock_iod nsiod; + unsigned timeout; + + lua_State *thread; + + const char *direction; + const char *action; + void *ssl_session; + struct sockaddr_storage source_addr; size_t source_addrlen; - struct nsock_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 */ + /* PCAP */ + int is_pcap; + nsock_event_id nseid; + struct timeval recvtime; /* Time packet was received, if r_success is true */ - struct ncap_socket *ncap_socket; - struct ncap_request *ncap_request; - int ncap_cback_ref; -}; +} nse_nsock_udata; -/* 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) +static int NSOCK_POOL = 0xac1dba11; + +static int gc_pool (lua_State *L) { - 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; + nsock_pool *nsp = (nsock_pool *) lua_touserdata(L, 1); + assert(*nsp != NULL); + nsp_delete(*nsp); + *nsp = NULL; + return 0; } -static void weak_table(lua_State * L, int narr, int nrec, const char *mode) +static nsock_pool new_pool (lua_State *L) { - lua_createtable(L, narr, nrec); - lua_createtable(L, 0, 1); - lua_pushstring(L, mode); - lua_setfield(L, -2, "__mode"); + nsock_pool nsp = nsp_new(NULL); + nsock_pool *nspp; + lua_pushlightuserdata(L, &NSOCK_POOL); + nspp = (nsock_pool *) lua_newuserdata(L, sizeof(nsock_pool)); + *nspp = nsp; + lua_newtable(L); + lua_pushcfunction(L, gc_pool); + lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + return nsp; } -static std::string hexify(const unsigned char *str, size_t len) +static nsock_pool get_pool (lua_State *L) +{ + nsock_pool *nsp; + lua_pushlightuserdata(L, &NSOCK_POOL); + lua_rawget(L, LUA_REGISTRYINDEX); + nsp = (nsock_pool *) lua_touserdata(L, -1); + assert(*nsp != NULL); + lua_pop(L, 1); + return *nsp; +} + +static std::string hexify (const unsigned char *str, size_t len) { size_t num = 0; @@ -241,20 +137,6 @@ static std::string hexify(const unsigned char *str, size_t len) return ret.str(); } -/* Thread index in nsock userdata environment. - */ -#define THREAD_I 1 - -static void set_thread (lua_State *L, int index, struct l_nsock_udata *n) -{ - lua_getfenv(L, index); - lua_pushthread(L); - lua_rawseti(L, -2, THREAD_I); - lua_pop(L, 1); /* nsock udata environment */ - n->yield.thread = L; -} - - /* Some constants used for enforcing a limit on the number of open sockets * in use by threads. The maximum value between MAX_PARALLELISM and * o.maxparallelism is the max # of threads that can have connected sockets @@ -270,8 +152,53 @@ static void set_thread (lua_State *L, int index, struct l_nsock_udata *n) * The table contains threads waiting to make a socket connection. */ #define MAX_PARALLELISM 20 -#define THREAD_SOCKETS 1 /* */ -#define CONNECT_WAITING 2 /* Threads waiting to lock */ + +/* The Lua 5.2 socket_lock function */ +#if 0 +/* int socket_lock (lua_State *L) + * + * This function is called by l_connect to get a "lock" on a socket. + * When connect calls this function, it expects socket_lock to yield forcing + * connect to be restarted when resumed or it succeeds returning normally. + */ +static void socket_lock (lua_State *L, int idx) +{ + unsigned p = o.max_parallelism == 0 ? MAX_PARALLELISM : o.max_parallelism; + int top = lua_gettop(L); + lua_rawgeti(L, LUA_ENVIRONINDEX, THREAD_SOCKETS); + nse_base(L); + lua_rawget(L, -2); + if (lua_istable(L, -1)) + { + /* Thread already has a "lock" with open sockets. Place the new socket + * in its sockets table */ + lua_pushvalue(L, idx); + lua_pushboolean(L, true); + lua_rawset(L, -3); + } else if (table_length(L, top+2) <= p) + { + /* There is room for this thread to open sockets */ + nse_base(L); + weak_table(L, 0, 0, "k"); /* weak socket references */ + lua_pushvalue(L, idx); /* socket */ + lua_pushboolean(L, true); + lua_rawset(L, -3); /* add to sockets table */ + lua_rawset(L, top+2); /* add new Pair + * to THREAD_SOCKETS */ + } else + { + /* Too many threads have sockets open. Add thread to waiting. The caller + * is expected to yield. (see the connect function in luaopen_nsock) */ + lua_rawgeti(L, LUA_ENVIRONINDEX, CONNECT_WAITING); + nse_base(L); + lua_pushboolean(L, true); + lua_settable(L, -3); + lua_settop(L, top); /* restore stack to original condition for l_connect */ + return nse_yield(L, 0, NULL); + } + lua_settop(L, top); /* restore stack to original condition for l_connect */ +} +#endif /* int socket_lock (lua_State *L) * @@ -281,7 +208,7 @@ static void set_thread (lua_State *L, int index, struct l_nsock_udata *n) * This function is called by Lua to get a "lock" on a socket. * See the connect function (in Lua) in luaopen_nsock. */ -static int socket_lock(lua_State * L) +static int socket_lock (lua_State *L) { unsigned p = o.max_parallelism == 0 ? MAX_PARALLELISM : o.max_parallelism; lua_settop(L, 1); @@ -313,13 +240,13 @@ static int socket_lock(lua_State * L) nse_base(L); lua_pushboolean(L, true); lua_settable(L, -3); - return nse_yield(L); + return nse_yield(L, 0, NULL); } lua_pushboolean(L, true); return 1; } -static void socket_unlock(lua_State * L) +static void socket_unlock (lua_State *L) { int top = lua_gettop(L); @@ -337,7 +264,7 @@ static void socket_unlock(lua_State * L) while (lua_next(L, -2) != 0) /* for each socket */ { lua_pop(L, 1); /* pop garbage boolean */ - if (((struct l_nsock_udata *) lua_touserdata(L, -1))->nsiod != NULL) + if (((nse_nsock_udata *) lua_touserdata(L, -1))->nsiod != NULL) open++; } } @@ -379,174 +306,134 @@ static void socket_unlock(lua_State * L) lua_settop(L, top); } -void l_nsock_clear_buf(lua_State * L, l_nsock_udata * udata); - -void l_nsock_ssl_reconnect_handler(nsock_pool nsp, nsock_event nse, void *yield) +static const char *inet_ntop_both (int af, const void *v_addr, char *ipstring) { - struct nsock_yield *y = (struct nsock_yield *) yield; - - lua_State *L = y->thread; - - if (lua_status(L) != LUA_YIELD) return; - - if (o.scriptTrace()) - l_nsock_trace(nse_iod(nse), "SSL RECONNECT", TO); - - if (l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) - nse_restore(y->thread, 1); + if (af == AF_INET) + { + inet_ntop(AF_INET, &((struct sockaddr_in *) v_addr)->sin_addr, + ipstring, INET6_ADDRSTRLEN); + return ipstring; + } +#ifdef HAVE_IPV6 + else if (af == AF_INET6) + { + inet_ntop(AF_INET6, &((struct sockaddr_in6 *) v_addr)->sin6_addr, + ipstring, INET6_ADDRSTRLEN); + return ipstring; + } +#endif else - nse_restore(y->thread, 2); + return "unknown protocol"; } -static int l_nsock_reconnect_ssl (lua_State *L) +static unsigned short inet_port_both (int af, const void *v_addr) { - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); + int port; - l_nsock_clear_buf(L, udata); - - if (udata->nsiod == NULL) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Trying to reconnect ssl through a closed socket\n"); - return 2; - } -#ifndef HAVE_OPENSSL - if (1) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Sorry, you don't have OpenSSL\n"); - return 2; - } + if (af == AF_INET) + port = ((struct sockaddr_in *) v_addr)->sin_port; +#ifdef HAVE_IPV6 + else if (af == AF_INET6) + port = ((struct sockaddr_in6 *) v_addr)->sin6_port; #endif + else + port = 0; - nsock_reconnect_ssl(nsp, udata->nsiod, l_nsock_ssl_reconnect_handler, - udata->timeout, &udata->yield, udata->ssl_session); - - set_thread(L, 1, udata); - return nse_yield(L); + return ntohs(port); } -int luaopen_nsock(lua_State * L) +#define TO ">" +#define FROM "<" + +static void trace (nsock_iod nsiod, const char *message, const char *dir) { - /* nsock:connect(socket, ...) This Lua function is a wrapper around the - * actual l_nsock_connect. The connect function must get a lock through - * socket_lock (C function above). Once it has the lock, it can (tail call) - * return the actual connect function. */ - static const char connect[] = - "local connect, socket_lock = ...;\n" - "return function(socket, ...)\n" - " repeat until socket_lock(socket) == true;\n" - " return connect(socket, ...);\n" "end"; - - static const luaL_Reg l_nsock[] = { - {"bind", l_nsock_bind}, - {"send", l_nsock_send}, - {"receive", l_nsock_receive}, - {"receive_lines", l_nsock_receive_lines}, - {"receive_bytes", l_nsock_receive_bytes}, - {"receive_buf", l_nsock_receive_buf}, - {"get_info", l_nsock_get_info}, - {"close", l_nsock_close}, - {"set_timeout", l_nsock_set_timeout}, - {"pcap_open", l_nsock_pcap_open}, - {"pcap_close", l_nsock_pcap_close}, - {"pcap_register", l_nsock_pcap_register}, - {"pcap_receive", l_nsock_pcap_receive}, - {"get_ssl_certificate", l_get_ssl_certificate}, - {"reconnect_ssl", l_nsock_reconnect_ssl}, - // {"callback_test", l_nsock_pcap_callback_test}, - {NULL, NULL} - }; - - /* Set up an environment for all nsock C functions to share. - * This table particularly contains the THREAD_SOCKETS and - * CONNECT_WAITING tables. - * These values are accessed at the Lua pseudo-index LUA_ENVIRONINDEX. - */ - lua_createtable(L, 2, 0); - lua_replace(L, LUA_ENVIRONINDEX); - - weak_table(L, 0, MAX_PARALLELISM, "k"); - lua_rawseti(L, LUA_ENVIRONINDEX, THREAD_SOCKETS); - - weak_table(L, 0, 1000, "k"); - lua_rawseti(L, LUA_ENVIRONINDEX, CONNECT_WAITING); - - lua_pushcfunction(L, l_nsock_loop); - lua_setfield(L, LUA_REGISTRYINDEX, NSE_NSOCK_LOOP); - - /* Load the connect function */ - if (luaL_loadstring(L, connect) != 0) - fatal("connect did not compile!"); - lua_pushcclosure(L, l_nsock_connect, 0); - lua_pushcclosure(L, socket_lock, 0); - 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 - - /* Create the nsock metatable for sockets */ - luaL_newmetatable(L, "nsock"); - lua_createtable(L, 0, 23); - luaL_register(L, NULL, l_nsock); - lua_pushvalue(L, -3); // connect function - lua_setfield(L, -2, "connect"); - lua_setfield(L, -2, "__index"); - lua_pushcclosure(L, l_nsock_gc, 0); - lua_setfield(L, -2, "__gc"); - lua_newtable(L); - lua_setfield(L, -2, "__metatable"); // protect metatable - lua_pop(L, 1); // nsock metatable - - luaL_newmetatable(L, "nsock_proxy"); - -#if HAVE_OPENSSL - /* Set up the SSL certificate userdata code in nse_ssl_cert.cc. */ - nse_nsock_init_ssl_cert(L); -#endif - - nsp = nsp_new(NULL); if (o.scriptTrace()) - nsp_settrace(nsp, NSOCK_TRACE_LEVEL, o.getStartTime()); -#if HAVE_OPENSSL - /* Value speed over security in SSL connections. */ - nsp_ssl_init_max_speed(nsp); -#endif + { + if (!nsi_is_pcap(nsiod)) + { + int status; + int protocol; + int af; + char ipstring_local[INET6_ADDRSTRLEN]; + char ipstring_remote[INET6_ADDRSTRLEN]; + struct sockaddr_storage local; + struct sockaddr_storage remote; - l_dnet_open(L); /* open dnet metatable */ - - return 0; + status = nsi_getlastcommunicationinfo(nsiod, &protocol, &af, + (sockaddr *) &local, (sockaddr *) &remote, sizeof(sockaddr_storage)); + log_write(LOG_STDOUT, "%s: %s %s:%d %s %s:%d | %s\n", + SCRIPT_ENGINE, + IPPROTO2STR_UC(protocol), + inet_ntop_both(af, &local, ipstring_local), + inet_port_both(af, &local), + dir, + inet_ntop_both(af, &remote, ipstring_remote), + inet_port_both(af, &remote), message); + } else { + log_write(LOG_STDOUT, "%s: %s | %s\n", SCRIPT_ENGINE, dir, message); + } + } } -int l_nsock_new(lua_State * L) +static void status (lua_State *L, enum nse_status status) { - struct l_nsock_udata *udata; - - udata = - (struct l_nsock_udata *) lua_newuserdata(L, sizeof(struct l_nsock_udata)); - luaL_getmetatable(L, "nsock"); - lua_setmetatable(L, -2); - lua_createtable(L, 1, 0); /* room for thread in array */ - lua_setfenv(L, -2); - udata->nsiod = NULL; - udata->ssl_session = NULL; - udata->source_addr.ss_family = AF_UNSPEC; - udata->source_addrlen = sizeof(udata->source_addr); - 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; - - return 1; + switch (status) + { + case NSE_STATUS_SUCCESS: + lua_pushboolean(L, true); + nse_restore(L, 1); + break; + case NSE_STATUS_KILL: + case NSE_STATUS_CANCELLED: + return; /* do nothing! */ + case NSE_STATUS_EOF: + case NSE_STATUS_ERROR: + case NSE_STATUS_TIMEOUT: + lua_pushnil(L); + lua_pushstring(L, nse_status2str(status)); + nse_restore(L, 2); + break; + case NSE_STATUS_NONE: + default: + assert(0); + break; + } } -static int l_nsock_loop(lua_State * L) +static void callback (nsock_pool nsp, nsock_event nse, void *ud) { + nse_nsock_udata *nu = (nse_nsock_udata *) ud; + lua_State *L = nu->thread; + assert(lua_status(L) == LUA_YIELD); + trace(nse_iod(nse), nu->action, nu->direction); + status(L, nse_status(nse)); +} + +static int yield (lua_State *L, nse_nsock_udata *nu, const char *action, + const char *direction, int ctx, lua_CFunction k) +{ + lua_getfenv(L, 1); + lua_pushthread(L); + lua_rawseti(L, -2, THREAD_I); + lua_pop(L, 1); /* nsock udata environment */ + nu->thread = L; + nu->action = action; + nu->direction = direction; + return nse_yield(L, ctx, k); +} + +static nse_nsock_udata *check_nsock_udata (lua_State *L, int idx, int open) +{ + nse_nsock_udata *nu = + (nse_nsock_udata *) luaL_checkudata(L, idx, NMAP_NSOCK_SOCKET); + if (open && nu->nsiod == NULL) + luaL_error(L, "socket must be connected\n"); + return nu; +} + +static int loop (lua_State *L) +{ + nsock_pool nsp = get_pool(L); int tout = luaL_checkint(L, 1); /* clean up old socket locks */ @@ -557,60 +444,303 @@ static int l_nsock_loop(lua_State * L) return 0; } -int l_nsock_checkstatus(lua_State * L, nsock_event nse) +static int l_reconnect_ssl (lua_State *L) { - enum nse_status status = nse_status(nse); + nsock_pool nsp = get_pool(L); + nse_nsock_udata *nu = check_nsock_udata(L, 1, 1); - switch (status) - { - case NSE_STATUS_SUCCESS: - lua_pushboolean(L, true); - return NSOCK_WRAPPER_SUCCESS; - break; - case NSE_STATUS_ERROR: - case NSE_STATUS_TIMEOUT: - case NSE_STATUS_CANCELLED: - case NSE_STATUS_KILL: - case NSE_STATUS_EOF: - lua_pushnil(L); - lua_pushstring(L, nse_status2str(status)); - return NSOCK_WRAPPER_ERROR; - break; - case NSE_STATUS_NONE: - default: - fatal("%s: In: %s:%i This should never happen.", - NSOCK_WRAPPER, __FILE__, __LINE__); - break; +#ifndef HAVE_OPENSSL + if (1) + return safe_error(L, "sorry, you don't have OpenSSL"); +#endif + nsock_reconnect_ssl(nsp, nu->nsiod, callback, nu->timeout, + nu, nu->ssl_session); + + return yield(L, nu, "SSL RECONNECT", TO, 0, NULL); +} + +static int l_connect (lua_State *L) +{ + enum type {TCP, UDP, SSL}; + static const char * const op[] = {"tcp", "udp", "ssl", NULL}; + + nsock_pool nsp = get_pool(L); + nse_nsock_udata *nu = check_nsock_udata(L, 1, 0); + const char *addr, *targetname; check_target(L, 2, &addr, &targetname); + const char *default_proto = NULL; + unsigned short port = check_port(L, 3, &default_proto); + if (default_proto == NULL) default_proto = "tcp"; + int what = luaL_checkoption(L, 4, default_proto, op); + struct addrinfo *dest; + int error_id; + + /* Lua 5.2 */ +#if 0 + /* either socket_lock yields and this function is resumed (and restarted) + * or it succeeds and we continue. + */ + socket_lock(L); +#endif + +#ifndef HAVE_OPENSSL + if (what == SSL) + return safe_error(L, "sorry, you don't have OpenSSL"); +#endif + + error_id = getaddrinfo(addr, NULL, NULL, &dest); + if (error_id) + return safe_error(L, gai_strerror(error_id)); + + if (dest == NULL) + return safe_error(L, "getaddrinfo returned success but no addresses"); + + nu->nsiod = nsi_new(nsp, NULL); + if (nu->source_addr.ss_family != AF_UNSPEC) { + nsi_set_localaddr(nu->nsiod, &nu->source_addr, nu->source_addrlen); + } else if (o.spoofsource) { + struct sockaddr_storage ss; + size_t sslen; + + o.SourceSockAddr(&ss, &sslen); + nsi_set_localaddr(nu->nsiod, &ss, sslen); + } + if (o.ipoptionslen) + nsi_set_ipoptions(nu->nsiod, o.ipoptions, o.ipoptionslen); + if (targetname != NULL) { + if (nsi_set_hostname(nu->nsiod, targetname) == -1) + fatal("nsi_set_hostname(\"%s\" failed in %s()", targetname, __func__); } - return -1; + switch (what) + { + case TCP: + nsock_connect_tcp(nsp, nu->nsiod, callback, nu->timeout, nu, + dest->ai_addr, dest->ai_addrlen, port); + break; + case UDP: + nsock_connect_udp(nsp, nu->nsiod, callback, nu, dest->ai_addr, + dest->ai_addrlen, port); + break; + case SSL: + nsock_connect_ssl(nsp, nu->nsiod, callback, nu->timeout, nu, + dest->ai_addr, dest->ai_addrlen, IPPROTO_TCP, port, nu->ssl_session); + break; + } + + freeaddrinfo(dest); + return yield(L, nu, "CONNECT", TO, 0, NULL); } +static int l_send (lua_State *L) +{ + nsock_pool nsp = get_pool(L); + nse_nsock_udata *nu = check_nsock_udata(L, 1, 1); + size_t size; + const char *string = luaL_checklstring(L, 2, &size); + trace(nu->nsiod, hexify((unsigned char *) string, size).c_str(), TO); + nsock_write(nsp, nu->nsiod, callback, nu->timeout, nu, string, size); + return yield(L, nu, "SEND", TO, 0, NULL); +} + +static void receive_callback (nsock_pool nsp, nsock_event nse, void *udata) +{ + nse_nsock_udata *nu = (nse_nsock_udata *) udata; + lua_State *L = nu->thread; + assert(lua_status(L) == LUA_YIELD); + if (nse_status(nse) == NSE_STATUS_SUCCESS) + { + int len; + const char *str = nse_readbuf(nse, &len); + trace(nse_iod(nse), hexify((const unsigned char *) str, len).c_str(), FROM); + lua_pushboolean(L, true); + lua_pushlstring(L, str, len); + nse_restore(L, 2); + } + else + status(L, nse_status(nse)); /* will also restore the thread */ +} + +static int l_receive (lua_State *L) +{ + nsock_pool nsp = get_pool(L); + nse_nsock_udata *nu = check_nsock_udata(L, 1, 1); + nsock_read(nsp, nu->nsiod, receive_callback, nu->timeout, nu); + return yield(L, nu, "RECEIVE", FROM, 0, NULL); +} + +static int l_receive_lines (lua_State *L) +{ + nsock_pool nsp = get_pool(L); + nse_nsock_udata *nu = check_nsock_udata(L, 1, 1); + nsock_readlines(nsp, nu->nsiod, receive_callback, nu->timeout, nu, + luaL_checkint(L, 2)); + return yield(L, nu, "RECEIVE LINES", FROM, 0, NULL); +} + +static int l_receive_bytes (lua_State *L) +{ + nsock_pool nsp = get_pool(L); + nse_nsock_udata *nu = check_nsock_udata(L, 1, 1); + nsock_readbytes(nsp, nu->nsiod, receive_callback, nu->timeout, nu, + luaL_checkint(L, 2)); + return yield(L, nu, "RECEIVE BYTES", FROM, 0, NULL); +} + +static int l_get_info (lua_State *L) +{ + nse_nsock_udata *nu = check_nsock_udata(L, 1, 1); + int status; + int protocol; // tcp or udp + int af; // address family + struct sockaddr local; + struct sockaddr remote; + char *ipstring_local = (char *) lua_newuserdata(L, sizeof(char) * INET6_ADDRSTRLEN); + char *ipstring_remote = (char *) lua_newuserdata(L, sizeof(char) * INET6_ADDRSTRLEN); + + status = nsi_getlastcommunicationinfo(nu->nsiod, &protocol, &af, + &local, &remote, sizeof(sockaddr)); + + lua_pushboolean(L, true); + lua_pushstring(L, inet_ntop_both(af, &local, ipstring_local)); + lua_pushnumber(L, inet_port_both(af, &local)); + lua_pushstring(L, inet_ntop_both(af, &remote, ipstring_remote)); + lua_pushnumber(L, inet_port_both(af, &remote)); + return 5; +} + +static int l_set_timeout (lua_State *L) +{ + nse_nsock_udata *nu = check_nsock_udata(L, 1, 0); + nu->timeout = luaL_checkint(L, 2); + return success(L); +} + +#if 0 +/* Lua 5.2 */ +static int l_receive_buf (lua_State *L) +{ + nsock_pool nsp = get_pool(L); + nse_nsock_udata *nu = check_nsock_udata(L, 1, 1); /* 1 */ + if (!(lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 2) == LUA_TSTRING)) + luaL_typeerror(L, 2, "function/string"); + luaL_checktype(L, 3, LUA_TBOOLEAN); /* 3 */ + + if (lua_getctx(L, NULL) == LUA_OK) + { + lua_settop(L, 3); /* clear top */ + lua_getuservalue(L, 1); /* 4 */ + lua_rawgeti(L, 4, BUFFER_I); /* 5 */ + } + else + { + /* Here we are returning from nsock_read below. + * We have two extra values on the stack pushed by receive_callback. + */ + assert(lua_gettop(L) == 7); + if (lua_toboolean(L, 6)) /* success? */ + { + lua_replace(L, 6); /* remove boolean */ + lua_concat(L, 2); /* concat BUFFER_I with received data */ + } + else /* receive_callback encountered an error */ + return 2; + } + + if (lua_isfunction(L, 2)) + { + lua_pushvalue(L, 2); + lua_pushvalue(L, 5); + lua_call(L, 1, 2); /* we do not allow yields */ + } + else /* string */ + { + lua_getglobal(L, "string"); + lua_getfield(L, -1, "find"); + lua_replace(L, -2); + lua_pushvalue(L, 5); + lua_pushvalue(L, 2); + lua_call(L, 2, 2); /* we do not allow yields */ + } + + if (lua_isnumber(L, -2) && lua_isnumber(L, -1)) /* found end? */ + { + lua_Integer l = lua_tointeger(L, -2), r = lua_tointeger(L, -1); + if (l > r || r > (lua_Integer) lua_objlen(L, 5)) + return luaL_error(L, "invalid indices for match"); + lua_pushboolean(L, 1); + if (lua_toboolean(L, 3)) + lua_pushlstring(L, lua_tostring(L, 5), r); + else + lua_pushlstring(L, lua_tostring(L, 5), l-1); + lua_pushlstring(L, lua_tostring(L, 5)+r, lua_objlen(L, 5)-r); + lua_rawseti(L, 4, BUFFER_I); + return 2; + } + else + { + lua_pop(L, 2); /* pop 2 results */ + nsock_read(nsp, nu->nsiod, receive_callback, nu->timeout, nu); + return yield(L, nu, "RECEIVE BUF", FROM, 0, l_receive_buf); + } +} +#endif + +static void sleep_callback (nsock_pool nsp, nsock_event nse, void *ud) +{ + lua_State *L = (lua_State *) ud; + assert(lua_status(L) == LUA_YIELD); + assert(nse_status(nse) == NSE_STATUS_SUCCESS); + nse_restore(L, 0); +} + +LUALIB_API int l_nsock_sleep (lua_State *L) +{ + nsock_pool nsp = get_pool(L); + double secs = luaL_checknumber(L, 1); + int msecs; + + if (secs < 0) + luaL_error(L, "argument to sleep (%f) must not be negative\n", secs); + /* Convert to milliseconds for nsock_timer_create. */ + msecs = (int) (secs * 1000 + 0.5); + nsock_timer_create(nsp, sleep_callback, msecs, L); + + return nse_yield(L, 0, NULL); +} + +#if HAVE_OPENSSL +SSL *nse_nsock_get_ssl (lua_State *L) +{ + nse_nsock_udata *nu = check_nsock_udata(L, 1, 0); + + if (!nsi_checkssl(nu->nsiod)) + luaL_argerror(L, 1, "not a SSL socket"); + + return (SSL *) nsi_getssl(nu->nsiod); +} +#else +/* If HAVE_OPENSSL is defined, this comes from nse_ssl_cert.cc. */ +int get_ssl_certificate (lua_State *L) +{ + return luaL_error("SSL is not available"); +} +#endif + /* Set the local address for socket operations. The two optional parameters after the first (which is the socket object) are a string representing a numeric address, and a port number. If either optional parameter is omitted or nil, that part of the address will be left unspecified. */ -static int l_nsock_bind(lua_State * L) +static int l_bind (lua_State *L) { + nse_nsock_udata *nu = check_nsock_udata(L, 1, 0); struct addrinfo hints = { 0 }; struct addrinfo *results; - char port_buf[16]; - l_nsock_udata *udata; - const char *addr_str = NULL; - const char *port_str = NULL; + const char *addr_str = luaL_optstring(L, 2, NULL); + luaL_checkint(L, 3); + const char *port_str = lua_tostring(L, 3); /* automatic conversion */ int rc; - udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - if (!lua_isnoneornil(L, 2)) - addr_str = luaL_checkstring(L, 2); - if (!lua_isnoneornil(L, 3)) { - int port; - port = luaL_checkint(L, 3); - Snprintf(port_buf, sizeof(port_buf), "%d", port); - port_str = port_buf; - } - /* If we don't have a string to work with, set our configured address family to get the proper unspecified address (0.0.0.0 or ::). Otherwise infer the family from the string. */ @@ -623,1644 +753,320 @@ static int l_nsock_bind(lua_State * L) hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; rc = getaddrinfo(addr_str, port_str, &hints, &results); - if (rc != 0) { - lua_pushnil(L); - lua_pushstring(L, gai_strerror(rc)); - return 2; - } - if (results == NULL) { - lua_pushnil(L); - lua_pushstring(L, "getaddrinfo: no results found"); - return 2; - } - if (results->ai_addrlen > sizeof(udata->source_addr)) { + if (rc != 0) + return safe_error(L, gai_strerror(rc)); + if (results == NULL) + return safe_error(L, "getaddrinfo: no results found"); + if (results->ai_addrlen > sizeof(nu->source_addr)) { freeaddrinfo(results); - lua_pushnil(L); - lua_pushstring(L, "getaddrinfo: result is too big"); - return 2; + return safe_error(L, "getaddrinfo: result is too big"); } /* We ignore any results after the first. */ - /* We would just call nsi_set_localaddr here, but udata->nsiod is not created + /* We would just call nsi_set_localaddr here, but nu->nsiod is not created until connect. So store the address in the userdatum. */ - udata->source_addrlen = results->ai_addrlen; - memcpy(&udata->source_addr, results->ai_addr, udata->source_addrlen); + nu->source_addrlen = results->ai_addrlen; + memcpy(&nu->source_addr, results->ai_addr, nu->source_addrlen); + + return success(L); +} + +static void initialize (lua_State *L, int idx, nse_nsock_udata *nu) +{ + lua_createtable(L, 2, 0); /* room for thread in array */ + lua_pushliteral(L, ""); + lua_rawseti(L, -2, BUFFER_I); + lua_setfenv(L, idx); + nu->nsiod = NULL; + nu->ssl_session = NULL; + nu->source_addr.ss_family = AF_UNSPEC; + nu->source_addrlen = sizeof(nu->source_addr); + nu->timeout = DEFAULT_TIMEOUT; + nu->is_pcap = 0; + nu->thread = NULL; + nu->direction = nu->action = NULL; +} + +LUALIB_API int l_nsock_new (lua_State *L) +{ + nse_nsock_udata *nu; + lua_settop(L, 0); + + nu = (nse_nsock_udata *) lua_newuserdata(L, sizeof(nse_nsock_udata)); + luaL_getmetatable(L, NMAP_NSOCK_SOCKET); + lua_setmetatable(L, -2); + initialize(L, 1, nu); - lua_pushboolean(L, 1); return 1; } -static int l_nsock_connect(lua_State * L) +static int l_close (lua_State *L) { - enum type {TCP, UDP, SSL}; - static const char * const op[] = {"tcp", "udp", "ssl", NULL}; - const char *default_proto = "tcp"; - - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - const char *addr, *targetname; - unsigned short port; - int what; - - addr = NULL; - targetname = NULL; - - /* host argument. */ - if (lua_istable(L, 2)) { - const char *ip; - - ip = NULL; - targetname = NULL; - - lua_getfield(L, 2, "ip"); - ip = lua_tostring(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 2, "targetname"); - targetname = lua_tostring(L, -1); - lua_pop(L, 1); - - if (ip != NULL) - addr = ip; - else if (targetname != NULL) - addr = targetname; - else - luaL_error(L, "host table does not have a 'ip' or 'targetname' field"); - } else { - addr = luaL_checkstring(L, 2); - targetname = addr; - } - - /* port argument. */ - if (lua_istable(L, 3)) { - lua_getfield(L, 3, "number"); - if (lua_isnil(L, -1)) - luaL_error(L, "port table does not have a 'number' field"); - else if (!lua_isnumber(L, -1)) - luaL_error(L, "port.number is not numeric"); - port = lua_tointeger(L, -1); - lua_pop(L, 1); - - lua_getfield(L, 3, "protocol"); - /* Make this the default if the "proto" argument isn't given. */ - if (lua_isstring(L, -1)) - default_proto = lua_tostring(L, -1); - lua_pop(L, 1); - } else { - port = (unsigned short) luaL_checkint(L, 3); - } - - /* proto argument. */ - what = luaL_checkoption(L, 4, default_proto, op); - - const char *error; - struct addrinfo *dest; - int error_id; - - l_nsock_clear_buf(L, udata); - -#ifndef HAVE_OPENSSL - if (what == SSL) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Sorry, you don't have OpenSSL\n"); - return 2; - } -#endif - - error_id = getaddrinfo(addr, NULL, NULL, &dest); - if (error_id) - { - error = gai_strerror(error_id); - lua_pushboolean(L, false); - lua_pushstring(L, error); - return 2; - } - if (dest == NULL) - { - lua_pushboolean(L, false); - lua_pushstring(L, "getaddrinfo returned success but no addresses"); - return 2; - } - - udata->nsiod = nsi_new(nsp, NULL); - if (udata->source_addr.ss_family != AF_UNSPEC) { - nsi_set_localaddr(udata->nsiod, &udata->source_addr, udata->source_addrlen); - } else if (o.spoofsource) { - struct sockaddr_storage ss; - size_t sslen; - - o.SourceSockAddr(&ss, &sslen); - nsi_set_localaddr(udata->nsiod, &ss, sslen); - } - if (targetname != NULL) { - if (nsi_set_hostname(udata->nsiod, targetname) == -1) - fatal("nsi_set_hostname(\"%s\" failed in %s()", targetname, __func__); - } - if (o.ipoptionslen) - nsi_set_ipoptions(udata->nsiod, o.ipoptions, o.ipoptionslen); - - switch (what) - { - case TCP: - nsock_connect_tcp(nsp, udata->nsiod, l_nsock_connect_handler, - udata->timeout, &udata->yield, dest->ai_addr, dest->ai_addrlen, port); - break; - case UDP: - nsock_connect_udp(nsp, udata->nsiod, l_nsock_connect_handler, - &udata->yield, dest->ai_addr, dest->ai_addrlen, port); - break; - case SSL: - nsock_connect_ssl(nsp, udata->nsiod, l_nsock_connect_handler, - udata->timeout, &udata->yield, dest->ai_addr, dest->ai_addrlen, - IPPROTO_TCP, port, udata->ssl_session); - break; - } - - freeaddrinfo(dest); - set_thread(L, 1, udata); - return nse_yield(L); -} - -void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *yield) -{ - struct nsock_yield *y = (struct nsock_yield *) yield; - - lua_State *L = y->thread; - - if (lua_status(L) != LUA_YIELD) return; - - if (o.scriptTrace()) - { - l_nsock_trace(nse_iod(nse), "CONNECT", TO); - } - - if (l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) - { - nse_restore(y->thread, 1); - } else - { - nse_restore(y->thread, 2); - } -} - -static int l_nsock_send(lua_State * L) -{ - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - const char *string = luaL_checkstring(L, 2); - - size_t string_len = lua_objlen(L, 2); - - l_nsock_clear_buf(L, udata); - - if (udata->nsiod == NULL) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Trying to send through a closed socket\n"); - return 2; - } - - if (o.scriptTrace()) - l_nsock_trace(udata->nsiod, hexify((unsigned char *) string, - string_len).c_str(), TO); - - nsock_write(nsp, udata->nsiod, l_nsock_send_handler, udata->timeout, - &udata->yield, string, string_len); - set_thread(L, 1, udata); - return nse_yield(L); -} - -void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *yield) -{ - struct nsock_yield *y = (struct nsock_yield *) yield; - - lua_State *L = y->thread; - - if (lua_status(L) != LUA_YIELD) return; - - if (l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) - { - nse_restore(y->thread, 1); - } else - { - nse_restore(y->thread, 2); - } -} - -static int l_nsock_receive(lua_State * L) -{ - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - l_nsock_clear_buf(L, udata); - - if (udata->nsiod == NULL) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Trying to receive through a closed socket\n"); - return 2; - } - - nsock_read(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, - &udata->yield); - - set_thread(L, 1, udata); - return nse_yield(L); -} - -static int l_nsock_receive_lines(lua_State * L) -{ - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - int nlines = (int) luaL_checknumber(L, 2); - - l_nsock_clear_buf(L, udata); - - if (udata->nsiod == NULL) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Trying to receive lines through a closed socket\n"); - return 2; - } - - nsock_readlines(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, - &udata->yield, nlines); - - set_thread(L, 1, udata); - return nse_yield(L); -} - -static int l_nsock_receive_bytes(lua_State * L) -{ - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - int nbytes = (int) luaL_checknumber(L, 2); - - l_nsock_clear_buf(L, udata); - - if (udata->nsiod == NULL) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Trying to receive bytes through a closed socket\n"); - return 2; - } - - nsock_readbytes(nsp, udata->nsiod, l_nsock_receive_handler, udata->timeout, - &udata->yield, nbytes); - - set_thread(L, 1, udata); - return nse_yield(L); -} - -void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *yield) -{ - struct nsock_yield *y = (struct nsock_yield *) yield; - - lua_State *L = y->thread; - - if (lua_status(L) != LUA_YIELD) return; - - char *rcvd_string; - - int rcvd_len = 0; - - if (l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) - { - rcvd_string = nse_readbuf(nse, &rcvd_len); - - if (o.scriptTrace()) - l_nsock_trace(nse_iod(nse), hexify((unsigned char *) rcvd_string, - (size_t) rcvd_len).c_str(), FROM); - - lua_pushlstring(L, rcvd_string, rcvd_len); - nse_restore(y->thread, 2); - } else - { - nse_restore(y->thread, 2); - } -} - -void l_nsock_trace(nsock_iod nsiod, const char *message, int direction) -{ - int status; - int protocol; - int af; - - if (!nsi_is_pcap(nsiod)) { - char ipstring_local[INET6_ADDRSTRLEN]; - char ipstring_remote[INET6_ADDRSTRLEN]; - struct sockaddr_storage local; - struct sockaddr_storage remote; - - status = nsi_getlastcommunicationinfo(nsiod, &protocol, &af, - (sockaddr *) &local, (sockaddr *) &remote, sizeof(sockaddr_storage)); - log_write(LOG_STDOUT, "%s: %s %s:%d %s %s:%d | %s\n", - SCRIPT_ENGINE, - IPPROTO2STR_UC(protocol), - inet_ntop_both(af, &local, ipstring_local), - inet_port_both(af, &local), - (direction == TO) ? ">" : "<", - inet_ntop_both(af, &remote, ipstring_remote), - inet_port_both(af, &remote), message); - } else { // is pcap device - log_write(LOG_STDOUT, "%s: %s | %s\n", - SCRIPT_ENGINE, (direction == TO) ? ">" : "<", message); - } -} - -const char *inet_ntop_both(int af, const void *v_addr, char *ipstring) -{ - if (af == AF_INET) - { - inet_ntop(AF_INET, &((struct sockaddr_in *) v_addr)->sin_addr, - ipstring, INET6_ADDRSTRLEN); - - return ipstring; - } -#ifdef HAVE_IPV6 - else if (af == AF_INET6) - { - inet_ntop(AF_INET6, &((struct sockaddr_in6 *) v_addr)->sin6_addr, - ipstring, INET6_ADDRSTRLEN); - return ipstring; - } -#endif - else - { - return "unknown protocol"; - } - -} - -unsigned short inet_port_both(int af, const void *v_addr) -{ - int port; - - if (af == AF_INET) - { - port = ((struct sockaddr_in *) v_addr)->sin_port; - } -#ifdef HAVE_IPV6 - else if (af == AF_INET6) - { - port = ((struct sockaddr_in6 *) v_addr)->sin6_port; - } -#endif - else - { - port = 0; - } - - return ntohs(port); -} - -static int l_nsock_get_info(lua_State * L) -{ - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - int status; - - int protocol; // tcp or udp - - int af; // address family - - struct sockaddr local; - - struct sockaddr remote; - - if (udata->nsiod == NULL) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Trying to get info from a closed socket\n"); - return 2; - } - - status = nsi_getlastcommunicationinfo(udata->nsiod, &protocol, &af, - &local, &remote, sizeof(sockaddr)); - - lua_pushboolean(L, true); - - char *ipstring_local = (char *) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN); - char *ipstring_remote = (char *) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN); - - lua_pushstring(L, inet_ntop_both(af, &local, ipstring_local)); - lua_pushnumber(L, inet_port_both(af, &local)); - - lua_pushstring(L, inet_ntop_both(af, &remote, ipstring_remote)); - lua_pushnumber(L, inet_port_both(af, &remote)); - - free(ipstring_local); - free(ipstring_remote); - return 5; -} - -static int l_nsock_gc(lua_State * L) -{ - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - if (udata->nsiod == NULL) - { // socket obviously got closed already - - // so no finalization needed - 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; -} - -static int l_nsock_close(lua_State * L) -{ - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - /* Never ever collect nse-pcap connections. */ - if (udata->ncap_socket) - { - return 0; - } - - l_nsock_clear_buf(L, udata); - - if (udata->nsiod == NULL) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Trying to close a closed socket\n"); - return 2; - } - - if (o.scriptTrace()) - { - l_nsock_trace(udata->nsiod, "CLOSE", TO); - } + nse_nsock_udata *nu = check_nsock_udata(L, 1, 0); + if (nu->nsiod == NULL) + return safe_error(L, "socket already closed"); + trace(nu->nsiod, "CLOSE", TO); #ifdef HAVE_OPENSSL - if (udata->ssl_session) - SSL_SESSION_free((SSL_SESSION *) udata->ssl_session); - udata->ssl_session = NULL; + if (nu->ssl_session) + SSL_SESSION_free((SSL_SESSION *) nu->ssl_session); #endif - - nsi_delete(udata->nsiod, NSOCK_PENDING_NOTIFY); - - udata->nsiod = NULL; - - lua_pushboolean(L, true); - return 1; + if (!nu->is_pcap) /* pcap sockets are closed by pcap_gc */ + nsi_delete(nu->nsiod, NSOCK_PENDING_NOTIFY); + initialize(L, 1, nu); + return success(L); } -static int l_nsock_set_timeout(lua_State * L) +static int nsock_gc (lua_State *L) { - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - int timeout = (unsigned short) luaL_checkint(L, 2); - - udata->timeout = timeout; - + nse_nsock_udata *nu = check_nsock_udata(L, 1, 0); + if (nu->nsiod) + return l_close(L); return 0; } -/* buffered I/O */ -static int l_nsock_receive_buf(lua_State * L) + +/****************** PCAP_SOCKET ***********************************************/ + +static void dnet_to_pcap_device_name (lua_State *L, const char *device) { - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - 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); - lua_pushstring(L, "Trying to receive through a closed socket\n"); - return 2; - } - if (udata->bufused == 0) - { - 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, - &udata->yield); - } else if (udata->bufused == -1) - { /* error message is inside the buffer */ - lua_pushboolean(L, false); - lua_rawgeti(L, LUA_REGISTRYINDEX, udata->bufidx); - return 2; - } else - { /* buffer contains already some data */ - /* we keep track here of how many calls to receive_buf are made */ - udata->bufused++; - if (l_nsock_check_buf(L) == NSOCK_WRAPPER_BUFFER_MOREREAD) - { - /* if we didn't have enough data in the buffer another nsock_read() was - * scheduled - its callback will put us in running state again */ - set_thread(L, 1, udata); - return nse_yield(L); - } - return 2; - } - set_thread(L, 1, udata); - return nse_yield(L); -} - -void l_nsock_receive_buf_handler(nsock_pool nsp, nsock_event nse, void *yield) -{ - struct nsock_yield *y = (struct nsock_yield *) yield; - - l_nsock_udata *udata = y->udata; - - lua_State *L = y->thread; - - if (lua_status(L) != LUA_YIELD) return; - - char *rcvd_string; - - int rcvd_len = 0; - - int tmpidx; - - /* 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 - // we do this on our own here - lua_pop(L, 1); - - rcvd_string = nse_readbuf(nse, &rcvd_len); - - if (o.scriptTrace()) - l_nsock_trace(nse_iod(nse), hexify((unsigned char *) rcvd_string, - (size_t) rcvd_len).c_str(), FROM); - /* push the buffer and what we received from nsock on the stack and - * concatenate both */ - lua_rawgeti(L, LUA_REGISTRYINDEX, udata->bufidx); - lua_pushlstring(L, rcvd_string, rcvd_len); - lua_concat(L, 2); - luaL_unref(L, LUA_REGISTRYINDEX, udata->bufidx); - udata->bufidx = luaL_ref(L, LUA_REGISTRYINDEX); - if (l_nsock_check_buf(L) == NSOCK_WRAPPER_BUFFER_MOREREAD) - { - /* if there wasn't enough data in the buffer and we've issued another - * nsock_read() the next callback will schedule the script for running */ - return; - } - nse_restore(y->thread, 2); - } else - { - if (udata->bufused > 1) - { - /* error occured after we read into some data into the buffer behave as - * if there was no error and push the rest of the buffer and clean the - * buffer afterwards */ - /* save the error message inside the buffer */ - tmpidx = luaL_ref(L, LUA_REGISTRYINDEX); - /* pop the status (==false) of the stack */ - lua_pop(L, 1); - lua_pushboolean(L, true); - lua_rawgeti(L, LUA_REGISTRYINDEX, udata->bufidx); - l_nsock_clear_buf(L, udata); - udata->bufidx = tmpidx; - udata->bufused = -1; - nse_restore(y->thread, 2); - } else - { /* buffer should be empty */ - nse_restore(y->thread, 2); - } - } -} - -int l_nsock_check_buf(lua_State * L) -{ - l_nsock_udata *udata; - - size_t startpos, endpos, bufsize; - - const char *tmpbuf; - - int tmpidx; - - int keeppattern; - - /* should we return the string including the pattern or without it */ - keeppattern = lua_toboolean(L, -1); - lua_pop(L, 1); - udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - if (lua_isfunction(L, 2)) - { - lua_pushvalue(L, 2); - lua_rawgeti(L, LUA_REGISTRYINDEX, udata->bufidx); /* the buffer is the - * only argument to the - * function */ - if (lua_pcall(L, 1, 2, 0) != 0) - { - lua_pushboolean(L, false); - lua_pushfstring(L, "Error inside splitting-function: %s\n", - lua_tostring(L, -1)); - return NSOCK_WRAPPER_BUFFER_OK; - // luaL_error(L,"Error inside splitting-function, given as argument to - // nsockobj:receive_buf: %s\n", lua_tostring(L,-1)); - } - } else if (lua_isstring(L, 2)) - { - lua_getglobal(L, "string"); - lua_getfield(L, -1, "find"); - lua_remove(L, -2); /* drop the string-table, since we don't - * need it! */ - lua_rawgeti(L, LUA_REGISTRYINDEX, udata->bufidx); - lua_pushvalue(L, 2); /* the pattern we are searching for */ - if (lua_pcall(L, 2, 2, 0) != 0) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Error in string.find (nsockobj:receive_buf)!"); - return NSOCK_WRAPPER_BUFFER_OK; - } - } else - { - lua_pushboolean(L, false); - lua_pushstring(L, "Expected either a function or a string!"); - return NSOCK_WRAPPER_BUFFER_OK; - // luaL_argerror(L,2,"expected either a function or a string!"); - } - /* 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, - &udata->yield); - lua_pushboolean(L, keeppattern); - return NSOCK_WRAPPER_BUFFER_MOREREAD; - } else - { - startpos = (size_t) lua_tointeger(L, -2); - endpos = (size_t) lua_tointeger(L, -1); - lua_settop(L, 0); /* clear the stack for returning */ - if (startpos > endpos) - { - lua_pushboolean(L, false); - lua_pushstring(L, "Delimiter has negative size!"); - return NSOCK_WRAPPER_BUFFER_OK; - } else if (startpos == endpos) - { - /* if the delimter has a size of zero we keep it, since otherwise - * retured string would be trucated */ - keeppattern = 1; - } - lua_settop(L, 0); /* clear the stack for returning */ - lua_rawgeti(L, LUA_REGISTRYINDEX, udata->bufidx); - tmpbuf = lua_tolstring(L, -1, &bufsize); - lua_pop(L, 1); /* pop the buffer off the stack, should - * be safe since it it is still in the - * registry */ - if (tmpbuf == NULL) - { - fatal - ("%s: In: %s:%i The buffer is not a string?! - please report this to nmap-dev@insecure.org.", - SCRIPT_ENGINE, __FILE__, __LINE__); - } - /* first push the remains of the buffer */ - lua_pushlstring(L, tmpbuf + endpos, (bufsize - endpos)); - tmpidx = luaL_ref(L, LUA_REGISTRYINDEX); - lua_pushboolean(L, true); - if (keeppattern) - { - lua_pushlstring(L, tmpbuf, endpos); - } else - { - lua_pushlstring(L, tmpbuf, startpos - 1); - } - luaL_unref(L, LUA_REGISTRYINDEX, udata->bufidx); - udata->bufidx = tmpidx; - // l_dumpStack(L); - return NSOCK_WRAPPER_BUFFER_OK; - } - assert(0); - return 1; // unreachable -} - -void l_nsock_clear_buf(lua_State * L, l_nsock_udata * udata) -{ - luaL_unref(L, LUA_REGISTRYINDEX, udata->bufidx); - udata->bufidx = LUA_NOREF; - udata->bufused = 0; -} - -static void l_nsock_sleep_handler(nsock_pool nsp, nsock_event nse, void *udata) -{ - lua_State *L = (lua_State *) udata; - - if (lua_status(L) != LUA_YIELD) return; - - assert(nse_status(nse) == NSE_STATUS_SUCCESS); - nse_restore(L, 0); -} - -int l_nsock_sleep(lua_State * L) -{ - double secs = luaL_checknumber(L, 1); - - int msecs; - - if (secs < 0) - luaL_error(L, "Argument to sleep (%f) must not be negative\n", secs); - /* Convert to milliseconds for nsock_timer_create. */ - msecs = (int) (secs * 1000 + 0.5); - nsock_timer_create(nsp, l_nsock_sleep_handler, msecs, L); - - return nse_yield(L); -} - -/****************** NCAP_SOCKET ***********************************************/ - -/* fuckin' C++ maps stuff */ -/* here we store ncap_sockets */ -std::map < std::string, struct ncap_socket *>ncap_socket_map; - -/* receive sthing from socket_map */ -struct ncap_socket *ncap_socket_map_get(char *key) -{ - std::string skey = key; - return ncap_socket_map[skey]; -} - -/* set sthing on socket_map */ -void ncap_socket_map_set(char *key, struct ncap_socket *ns) -{ - std::string skey = key; - ncap_socket_map[skey] = ns; - return; -} - -/* receive sthing from socket_map */ -void ncap_socket_map_del(char *key) -{ - std::string skey = key; - ncap_socket_map.erase(skey); - return; -} - -/* (static) Dnet-like device name to Pcap-like name */ -char *dnet_to_pcap_device_name(const char *device) -{ - static char pcapdev[128]; - if (strcmp(device, "any") == 0) - return strncpy(pcapdev, "any", sizeof(pcapdev)); - + lua_pushliteral(L, "any"); + else #ifdef WIN32 - /* Nmap normally uses device names obtained through dnet for interfaces, but - * Pcap has its own naming system. So the conversion is done here */ - if (!DnetName2PcapName(device, pcapdev, sizeof(pcapdev))) { - /* Oh crap -- couldn't find the corresponding dev apparently. Let's just - * go with what we have then ... */ - strncpy(pcapdev, device, sizeof(pcapdev)); + char pcapdev[4096]; + /* Nmap normally uses device names obtained through dnet for interfaces, + but Pcap has its own naming system. So the conversion is done here */ + if (!DnetName2PcapName(device, pcapdev, sizeof(pcapdev))) + lua_pushstring(L, device); + else + lua_pushstring(L, pcapdev); } #else - strncpy(pcapdev, device, sizeof(pcapdev)); + lua_pushstring(L, device); #endif - return pcapdev; } -/* (LUA) Open nsock-pcap socket. - * 1) device - dnet-style network interface name, or "any" - * 2) snaplen - maximum number of bytes to be captured for packet - * 3) promisc - should we set network car in promiscuous mode (0/1) - * 4) callback- callback function, that will create hash string from packet - * 5) bpf - berkeley packet filter, see tcpdump(8) - * */ -static int l_nsock_pcap_open(lua_State * L) +static int pcap_gc (lua_State *L) { - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); + nsock_iod *nsiod = (nsock_iod *) lua_touserdata(L, 1); + nsi_delete(*nsiod, NSOCK_PENDING_NOTIFY); + *nsiod = NULL; + return 0; +} +static int l_pcap_open (lua_State *L) +{ + nsock_pool nsp = get_pool(L); + nse_nsock_udata *nu = check_nsock_udata(L, 1, 0); const char *device = luaL_checkstring(L, 2); - int snaplen = luaL_checkint(L, 3); + luaL_checktype(L, 4, LUA_TBOOLEAN); /* promiscuous */ + const char *bpf = luaL_checkstring(L, 5); - int promisc = luaL_checkint(L, 4); + lua_settop(L, 5); - luaL_checktype(L, 5, LUA_TFUNCTION); /* callback function that creates hash */ - const char *bpf = luaL_checkstring(L, 6); + dnet_to_pcap_device_name(L, device); /* 6 */ + lua_pushfstring(L, "%s|%d|%d|%s", lua_tostring(L, 6), snaplen, + lua_toboolean(L, 4), lua_tostring(L, 5)); /* 7, the pcap socket key */ - if (udata->nsiod || udata->ncap_request || udata->ncap_socket) + if (nu->nsiod) + luaL_argerror(L, 1, "socket is already open"); + + if (lua_objlen(L, 6) == 0) + luaL_argerror(L, 2, "bad device name"); + + lua_rawgeti(L, LUA_ENVIRONINDEX, KEY_PCAP); + lua_pushvalue(L, 7); + lua_rawget(L, -2); + nsock_iod *nsiod = (nsock_iod *) lua_touserdata(L, -1); + if (nsiod == NULL) /* does not exist */ { - luaL_argerror(L, 1, - "Trying to open nsock-pcap, but this connection is already opened"); - return 0; + nsiod = (nsock_iod *) lua_newuserdata(L, sizeof(nsock_iod)); + luaL_getmetatable(L, NMAP_NSOCK_PCAP_SOCKET); + lua_setmetatable(L, -2); + *nsiod = nsi_new(nsp, nu); + lua_rawgeti(L, LUA_ENVIRONINDEX, KEY_PCAP); + lua_pushvalue(L, 7); /* the pcap socket key */ + lua_pushvalue(L, -2); /* the pcap socket nsiod */ + lua_rawset(L, -3); /* _ENV[KEY_PCAP]["dev|snap|promis|bpf"] = pcap_nsiod */ + lua_pop(L, 1); /* KEY_PCAP */ + lua_getfenv(L, 1); /* the socket user value */ + lua_pushvalue(L, -2); /* the pcap socket nsiod */ + lua_pushboolean(L, 1); /* dummy variable */ + lua_rawset(L, -3); + lua_pop(L, 1); /* the socket user value */ + char *e = nsock_pcap_open(nsp, *nsiod, lua_tostring(L, 6), snaplen, + lua_toboolean(L, 4), bpf); + if (e) + luaL_error(L, "%s", e); } - char *pcapdev = dnet_to_pcap_device_name(device); - - if (!strlen(device) || !strlen(pcapdev)) - { - luaL_argerror(L, 1, - "Trying to open nsock-pcap, but you're passing empty or wrong device name."); - return 0; - } - - lua_pop(L, 1); // pop bpf - /* take func from top of stack and store it in the Registry */ - int hash_func_ref = luaL_ref(L, LUA_REGISTRYINDEX); - - /* push function on the registry-stack */ - lua_rawgeti(L, LUA_REGISTRYINDEX, hash_func_ref); - - struct ncap_socket *ns; - - /* create key */ - char key[8192]; - - Snprintf(key, sizeof(key), "%s|%i|%i|%u|%s", - pcapdev, snaplen, promisc, (unsigned int) strlen(bpf), bpf); - ns = ncap_socket_map_get(key); - if (ns == NULL) - { - ns = (struct ncap_socket *) safe_zalloc(sizeof(struct ncap_socket)); - ns->nsiod = nsi_new(nsp, ns); - ns->key = strdup(key); - /* error messages are passed here */ - char *emsg = - nsock_pcap_open(nsp, ns->nsiod, pcapdev, snaplen, promisc, bpf); - if (emsg) - { - luaL_argerror(L, 1, emsg); - return 0; - } - ncap_socket_map_set(key, ns); - } - ns->references++; - udata->nsiod = ns->nsiod; - udata->ncap_socket = ns; - udata->ncap_cback_ref = hash_func_ref; + nu->nsiod = *nsiod; + nu->is_pcap = 1; return 0; } -/* (LUA) Close nsock-pcap socket. - * */ -static int l_nsock_pcap_close(lua_State * L) +static void pcap_receive_handler (nsock_pool nsp, nsock_event nse, void *ud) { - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); + nse_nsock_udata *nu = (nse_nsock_udata *) ud; + lua_State *L = nu->thread; - struct ncap_socket *ns = udata->ncap_socket; - - if (!udata->nsiod || !udata->ncap_socket) + assert(lua_status(L) == LUA_YIELD); + if (nse_status(nse) == NSE_STATUS_SUCCESS) { - luaL_argerror(L, 1, "Trying to close nsock-pcap, but it was never opened."); - return 0; - } - if (udata->ncap_request) - { - luaL_argerror(L, 1, "Trying to close nsock-pcap, but it has active event."); - return 0; - } + const unsigned char *l2_data, *l3_data; + size_t l2_len, l3_len, packet_len; + struct timeval tv; - assert(ns->references > 0); + nse_readpcap(nse, &l2_data, &l2_len, &l3_data, &l3_len, &packet_len, &tv); - ns->references--; - if (ns->references == 0) - { - if (ns->key) { - ncap_socket_map_del(ns->key); - free(ns->key); - } - nsi_delete(ns->nsiod, NSOCK_PENDING_NOTIFY); - free(ns); - } - - udata->nsiod = NULL; - udata->ncap_socket = NULL; - lua_unref(L, udata->ncap_cback_ref); - udata->ncap_cback_ref = 0; - - lua_pushboolean(L, true); - return 1; -} - -/* (static) binary string to hex zero-terminated string */ -char *hex(char *str, unsigned int strsz) -{ - static char x[] = - { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', - 'e', 'f' }; - static char buf[2048]; - - unsigned int i; - - unsigned char *s; - - for (i = 0, s = (unsigned char *) str; i < strsz && i < (sizeof(buf) / 2 - 1); - i++, s++) - { - buf[i * 2] = x[*s / 16]; - buf[i * 2 + 1] = x[*s % 16]; - } - buf[i * 2] = '\0'; - return (buf); -} - -/****************** NCAP_REQUEST **********************************************/ - -int ncap_restore_lua(ncap_request * nr); - -void ncap_request_set_result(nsock_event nse, struct ncap_request *nr); - -int ncap_request_set_results(nsock_event nse, const char *key); - -void l_nsock_pcap_receive_handler(nsock_pool nsp, nsock_event nse, - void *userdata); - -/* next map, this time it's multimap "key"(from callback)->suspended_lua_threads */ -std::multimap < std::string, struct ncap_request *>ncap_request_map; - -typedef std::multimap < std::string, - struct ncap_request *>::iterator ncap_request_map_iterator; -typedef std::pair < ncap_request_map_iterator, - ncap_request_map_iterator > ncap_request_map_ii; - -/* del from multimap */ -void ncap_request_map_del(struct ncap_request *nr) -{ - ncap_request_map_iterator i; - - ncap_request_map_ii ii; - - std::string s = nr->key; - ii = ncap_request_map.equal_range(s); - - for (i = ii.first; i != ii.second; i++) - { - if (i->second == nr) - { - i->second = NULL; - ncap_request_map.erase(i); - return; - } - } - assert(0); -} - -/* add to multimap */ -void ncap_request_map_add(char *key, struct ncap_request *nr) -{ - std::string skey = key; - ncap_request_map.insert(std::pair < std::string, struct ncap_request *>(skey, - nr)); - return; -} - -/* (LUA) Register event that will wait for one packet matching hash. - * It's non-blocking method of capturing packets. - * 1) hash - hash for packet that should be matched. or empty string if you - * want to receive first packet - * */ -static int l_nsock_pcap_register(lua_State * L) -{ - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - size_t testdatasz; - - const char *testdata = luaL_checklstring(L, 2, &testdatasz); - - struct timeval now = *nsock_gettimeofday(); - - if (!udata->nsiod || !udata->ncap_socket) - { - luaL_argerror(L, 1, - "You can't register to nsock-pcap if it wasn't opened."); - return 0; - } - if (udata->ncap_request) - { - luaL_argerror(L, 1, "You are already registered to this socket."); - return 0; - } - - struct ncap_request *nr = - (struct ncap_request *) safe_zalloc(sizeof(struct ncap_request)); - - udata->ncap_request = nr; - - TIMEVAL_MSEC_ADD(nr->end_time, now, udata->timeout); - nr->key = strdup(hex((char *) testdata, testdatasz)); - nr->yield = &udata->yield; - set_thread(L, 1, udata); - udata->yield.udata = udata; - nr->ncap_cback_ref = udata->ncap_cback_ref; - /* always create new event. */ - nr->nseid = nsock_pcap_read_packet(nsp, - udata->nsiod, l_nsock_pcap_receive_handler, udata->timeout, nr); - - ncap_request_map_add(nr->key, nr); - - /* that's it. return to lua */ - return 0; -} - -/* (LUA) After "register" use this function to block, and wait for packet. - * If packet is already captured, this function will return immidietly. - * - * return values: status(true/false), capture_len/error_msg, layer2data, layer3data - * */ -int l_nsock_pcap_receive(lua_State * L) -{ - l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - - if (!udata->nsiod || !udata->ncap_socket) - { - luaL_argerror(L, 1, "You can't receive to nsock-pcap if it wasn't opened."); - return 0; - } - if (!udata->ncap_request) - { - luaL_argerror(L, 1, "You can't it's not registered"); - return 0; - } - - /* and clear udata->ncap_request, we'll never,ever have access to current - * udata during this request */ - struct ncap_request *nr = udata->ncap_request; - - udata->ncap_request = NULL; - set_thread(L, 1, udata); - udata->yield.udata = udata; - - /* ready to receive data? don't suspend thread */ - if (nr->received) /* data already received */ - return ncap_restore_lua(nr); - - /* no data yet? suspend thread */ - nr->suspended = 1; - - return nse_yield(L); -} - -/* (free) excute callback function from lua script */ -char *ncap_request_do_callback(nsock_event nse, lua_State * L, - int ncap_cback_ref) -{ - const unsigned char *l2_data, *l3_data; - - size_t l2_len, l3_len, packet_len; - - nse_readpcap(nse, &l2_data, &l2_len, &l3_data, &l3_len, &packet_len, NULL); - - lua_rawgeti(L, LUA_REGISTRYINDEX, ncap_cback_ref); - lua_pushnumber(L, packet_len); - lua_pushlstring(L, (char *) l2_data, l2_len); - lua_pushlstring(L, (char *) l3_data, l3_len); - - lua_call(L, 3, 1); - - /* get string from top of the stack */ - size_t testdatasz; - - const char *testdata = lua_tolstring(L, -1, &testdatasz); - - char *key = strdup(hex((char *) testdata, testdatasz)); - - return key; -} - -/* callback from nsock */ -void l_nsock_pcap_receive_handler(nsock_pool nsp, nsock_event nse, - void *userdata) -{ - int this_event_restored = 0; - - struct ncap_request *nr = (struct ncap_request *) userdata; - - switch (nse_status(nse)) - { - case NSE_STATUS_SUCCESS: - { - 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, ""); - - /* process everything that matches test */ - this_event_restored += ncap_request_set_results(nse, key); - free(key); - - if (!this_event_restored) - { - /* okay, we received event but it wasn't handled by the process that - * requested this event. We must query for new event with smaller - * timeout */ - struct timeval now = *nsock_gettimeofday(); - - /* event was successfull so I assert it occured before pr->end_time */ - int timeout = TIMEVAL_MSEC_SUBTRACT(nr->end_time, now); - - if (timeout < 0) /* funny to receive event that should be - * timeouted in the past. But on windows - * it can happen */ - timeout = 0; - nr->nseid = nsock_pcap_read_packet(nsp, - nse_iod(nse), l_nsock_pcap_receive_handler, timeout, nr); - /* no need to cancel or delete current nse :) */ - } - return; - } - default: - /* event timeouted */ - ncap_request_map_del(nr); /* delete from map */ - ncap_request_set_result(nse, nr); - if (nr->suspended) /* restore thread */ - ncap_restore_lua(nr); - return; - } -} - -/* get data from nsock_event, and set result on ncap_requests which mach key */ -int ncap_request_set_results(nsock_event nse, const char *key) -{ - int this_event_restored = 0; - - std::string skey = key; - - ncap_request_map_iterator i; - - ncap_request_map_ii ii; - - ii = ncap_request_map.equal_range(skey); - for (i = ii.first; i != ii.second; i++) - { - /* tests are successfull, so just restore process */ - ncap_request *nr = i->second; - - if (nr->nseid == nse_id(nse)) - this_event_restored = 1; - - ncap_request_set_result(nse, nr); - if (nr->suspended) - ncap_restore_lua(nr); - } - ncap_request_map.erase(ii.first, ii.second); - - return this_event_restored; -} - -/* get data from nsock_event, and set result ncap_request */ -void ncap_request_set_result(nsock_event nse, struct ncap_request *nr) -{ - enum nse_status status = nse_status(nse); - - nr->received = true; - - switch (status) - { - case NSE_STATUS_SUCCESS: - { - nr->r_success = true; - - const unsigned char *l2_data, *l3_data; - - size_t l2_len, l3_len, packet_len; - struct timeval tv; - - nse_readpcap(nse, &l2_data, &l2_len, &l3_data, &l3_len, - &packet_len, &tv); - char *packet = (char *) safe_malloc(l2_len + l3_len); - - nr->r_layer2 = (unsigned char *) packet; - memcpy(nr->r_layer2, l2_data, l2_len); - nr->r_layer3 = (unsigned char *) (packet + l2_len); - memcpy(nr->r_layer3, l3_data, l3_len); - nr->r_layer2_len = l2_len; - nr->r_layer3_len = l3_len; - nr->packetsz = packet_len; - nr->recvtime = tv; - break; - } - case NSE_STATUS_ERROR: - case NSE_STATUS_TIMEOUT: - case NSE_STATUS_CANCELLED: - case NSE_STATUS_KILL: - case NSE_STATUS_EOF: - nr->r_success = false; - nr->r_status = strdup(nse_status2str(status)); - break; - case NSE_STATUS_NONE: - default: - fatal("%s: In: %s:%i This should never happen.", - NSOCK_WRAPPER, __FILE__, __LINE__); - } - - if (nr->nseid != nse_id(nse)) - { /* different event, cancel */ - nsock_event_cancel(nsp, nr->nseid, 0); /* Don't send CANCELED event, just - * cancel */ - nr->nseid = 0; - } else - { /* this event -> do nothing */ - } - - return; -} - -/* 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->yield->thread; - - if (nr->r_success) - { - lua_pushboolean(L, true); - lua_pushnumber(L, nr->packetsz); - lua_pushlstring(L, (char *) nr->r_layer2, nr->r_layer2_len); - lua_pushlstring(L, (char *) nr->r_layer3, nr->r_layer3_len); - lua_pushnumber(L, TIMEVAL_SECS(nr->recvtime)); - } else - { - lua_pushnil(L); - lua_pushstring(L, nr->r_status); - lua_pushnil(L); - lua_pushnil(L); - lua_pushnil(L); - } - bool suspended = nr->suspended; - - // 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); - if (nr->r_layer2) - free(nr->r_layer2); - /* dont' free r_layer3, it's in the same block as r_layer2 */ - - free(nr); - - if (suspended) /* lua process is suspended */ + lua_pushboolean(L, 1); + lua_pushinteger(L, packet_len); + lua_pushlstring(L, (const char *) l2_data, l2_len); + lua_pushlstring(L, (const char *) l3_data, l3_len); + lua_pushnumber(L, TIMEVAL_SECS(tv)); nse_restore(L, 5); - else /* not suspended, just pass output */ - return 5; - return 0; + } + else + status(L, nse_status(nse)); /* will also restore the thread */ } +static int l_pcap_receive (lua_State *L) +{ + nsock_pool nsp = get_pool(L); + nse_nsock_udata *nu = check_nsock_udata(L, 1, 1); + nu->nseid = nsock_pcap_read_packet(nsp, nu->nsiod, pcap_receive_handler, + nu->timeout, nu); + return yield(L, nu, "PCAP RECEIVE", FROM, 0, NULL); +} + +LUALIB_API int luaopen_nsock (lua_State *L) +{ +/* These two functions can be implemented in C in Lua 5.2 */ + + /* nsock:connect(socket, ...) + * This Lua function is a wrapper around the actual l_nsock_connect. The + * connect function must get a lock through socket_lock (C function above). + * Once it has the lock, it can (tail call) return the actual connect + * function. + */ + static const char connect[] = +"local connect, socket_lock = ...;\n" +"return function(socket, ...)\n" +" repeat until socket_lock(socket) == true;\n" +" return connect(socket, ...);\n" +"end\n"; + static const char receive_buf[] = +"local function receive_buf (socket, fstr, keep)\n" +" local i, j;\n" +" local socket_uservalue = debug.getfenv(socket);\n" +" local buf = socket_uservalue[2];\n" +" if type(fstr) == 'function' then\n" +" i, j = fstr(buf);\n" +" elseif type(fstr) == 'string' then\n" +" i, j = string.find(buf, fstr)\n" +" end\n" +" if type(i) == 'number' and type(j) == 'number' then\n" +" if i > j or j > #buf then\n" +" error('invalid indices for match');\n" +" else\n" +" socket_uservalue[2] = string.sub(buf, j+1);\n" +" if keep then\n" +" return true, string.sub(buf, 1, j);\n" +" else\n" +" return true, string.sub(buf, 1, i-1);\n" +" end\n" +" end\n" +" else\n" +" local status, result = socket:receive();\n" +" if not status then return status, result end\n" +" socket_uservalue[2] = socket_uservalue[2]..result;\n" +" return receive_buf(socket, fstr, keep);\n" +" end\n" +"end\n" +"return receive_buf;\n"; + + static const luaL_Reg l_nsock[] = { + {"bind", l_bind}, + {"send", l_send}, + {"receive", l_receive}, + {"receive_lines", l_receive_lines}, + {"receive_bytes", l_receive_bytes}, + /* {"receive_buf", l_receive_buf}, Lua 5.2 */ + {"get_info", l_get_info}, + {"close", l_close}, + {"set_timeout", l_set_timeout}, + {"reconnect_ssl", l_reconnect_ssl}, + {"get_ssl_certificate", l_get_ssl_certificate}, + {"pcap_open", l_pcap_open}, + {"pcap_close", l_close}, + {"pcap_receive", l_pcap_receive}, + {NULL, NULL} + }; + + /* Set up an environment for all nsock C functions to share. + * This table particularly contains the THREAD_SOCKETS and + * CONNECT_WAITING tables. + * These values are accessed at the Lua pseudo-index LUA_ENVIRONINDEX. + */ + lua_createtable(L, 3, 0); + lua_replace(L, LUA_ENVIRONINDEX); + + weak_table(L, 0, MAX_PARALLELISM, "k"); + lua_rawseti(L, LUA_ENVIRONINDEX, THREAD_SOCKETS); + + weak_table(L, 0, 1000, "k"); + lua_rawseti(L, LUA_ENVIRONINDEX, CONNECT_WAITING); + + weak_table(L, 0, 0, "v"); + lua_rawseti(L, LUA_ENVIRONINDEX, KEY_PCAP); + + lua_pushcfunction(L, loop); + lua_setfield(L, LUA_REGISTRYINDEX, NSE_NSOCK_LOOP); + + /* Load the connect function */ + if (luaL_loadstring(L, connect) != 0) + assert(0); + lua_pushcfunction(L, l_connect); + lua_pushcfunction(L, socket_lock); + lua_call(L, 2, 1); // leave connect function on stack... + + /* Create the nsock metatable for sockets */ + luaL_newmetatable(L, NMAP_NSOCK_SOCKET); + lua_createtable(L, 0, 23); + luaL_register(L, NULL, l_nsock); + lua_pushvalue(L, -3); // connect function + lua_setfield(L, -2, "connect"); + if (luaL_dostring(L, receive_buf)) + assert(0); + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setfenv(L, -2); + lua_setfield(L, -2, "receive_buf"); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, nsock_gc); + lua_setfield(L, -2, "__gc"); + lua_newtable(L); + lua_setfield(L, -2, "__metatable"); // protect metatable + lua_pop(L, 1); // nsock metatable + + /* Create the nsock pcap metatable */ + luaL_newmetatable(L, NMAP_NSOCK_PCAP_SOCKET); + lua_pushcfunction(L, pcap_gc); + lua_setfield(L, -2, "__gc"); + lua_pop(L, 1); + #if HAVE_OPENSSL -SSL *nse_nsock_get_ssl(lua_State *L) -{ - const l_nsock_udata *udata; - - udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock"); - if (!nsi_checkssl(udata->nsiod)) - error("Socket is not an SSL socket"); - - return (SSL *) nsi_getssl(udata->nsiod); -} -#else -/* If HAVE_OPENSSL is defined, this comes from nse_ssl_cert.cc. */ -int l_get_ssl_certificate(lua_State *L) -{ - error("Socket is not an SSL socket"); - - return 0; -} + /* Set up the SSL certificate userdata code in nse_ssl_cert.cc. */ + nse_nsock_init_ssl_cert(L); #endif -/****************** DNET ******************************************************/ -static int l_dnet_open_ethernet(lua_State * L); -static int l_dnet_close_ethernet(lua_State * L); -static int l_dnet_send_ethernet(lua_State * L); -static int l_dnet_open_ip(lua_State * L); -static int l_dnet_close_ip(lua_State * L); -static int l_dnet_send_ip(lua_State * L); - -static luaL_reg l_dnet[] = { - {"ethernet_open", l_dnet_open_ethernet}, - {"ethernet_close", l_dnet_close_ethernet}, - {"ethernet_send", l_dnet_send_ethernet}, - {"ip_open", l_dnet_open_ip}, - {"ip_close", l_dnet_close_ip}, - {"ip_send", l_dnet_send_ip}, - {NULL, NULL} -}; - -void l_dnet_open(lua_State * L) -{ - luaL_newmetatable(L, "dnet"); - lua_createtable(L, 0, 5); - luaL_register(L, NULL, l_dnet); - lua_setfield(L, -2, "__index"); - lua_pushliteral(L, ""); - lua_setfield(L, -2, "__metatable"); // protect metatable - lua_pop(L, 1); -} - -struct l_dnet_udata -{ - char *interface; - eth_t *eth; - int sock; // raw ip socket -}; - -int l_dnet_new(lua_State * L) -{ - struct l_dnet_udata *udata; - - udata = - (struct l_dnet_udata *) lua_newuserdata(L, sizeof(struct l_dnet_udata)); - luaL_getmetatable(L, "dnet"); - lua_setmetatable(L, -2); - udata->interface = NULL; - udata->eth = NULL; - udata->sock = -1; - - return 1; -} - -int l_dnet_get_interface_link(lua_State * L) -{ - const char *interface_name = luaL_checkstring(L, 1); - - struct interface_info *ii = getInterfaceByName((char *) interface_name); - - if (!ii) - { - lua_pushnil(L); - return 1; - } - const char *s = NULL; - - switch (ii->device_type) - { - case devt_ethernet: - s = "ethernet"; - break; - case devt_loopback: - s = "loopback"; - break; - case devt_p2p: - s = "p2p"; - break; - case devt_other: - default: - s = NULL; - break; - } - if (s) - lua_pushstring(L, s); - else - lua_pushnil(L); - - return 1; -} - -typedef struct -{ - int references; - eth_t *eth; -} dnet_eth_map; - -std::map < std::string, dnet_eth_map * >dnet_eth_cache; - -eth_t *ldnet_eth_open_cached(const char *device) -{ - assert(device && *device); - - std::string key = device; - dnet_eth_map *dem = dnet_eth_cache[key]; - - if (dem != NULL) - { - dem->references++; - return dem->eth; - } - - dem = (dnet_eth_map *) safe_zalloc(sizeof(dnet_eth_map)); - dem->eth = eth_open(device); - if (!dem->eth) - fatal("Unable to open dnet on ethernet interface %s", device); - dem->references = 1; - dnet_eth_cache[key] = dem; - return dem->eth; -} - -/* See the description for eth_open_cached */ -void ldnet_eth_close_cached(const char *device) -{ - std::string key = device; - dnet_eth_map *dem = dnet_eth_cache[key]; - - assert(dem); - dem->references--; - if (dem->references == 0) - { - dnet_eth_cache.erase(key); - eth_close(dem->eth); - free(dem); - } - return; -} - -static int l_dnet_open_ethernet(lua_State * L) -{ - l_dnet_udata *udata = (l_dnet_udata *) luaL_checkudata(L, 1, "dnet"); - - const char *interface_name = luaL_checkstring(L, 2); - - struct interface_info *ii = getInterfaceByName((char *) interface_name); - - if (!ii || ii->device_type != devt_ethernet) - { - luaL_argerror(L, 2, "device is not valid ethernet interface"); - return 0; - } - udata->interface = strdup(interface_name); - udata->eth = ldnet_eth_open_cached(interface_name); - - return 0; -} - -static int l_dnet_close_ethernet(lua_State * L) -{ - l_dnet_udata *udata = (l_dnet_udata *) luaL_checkudata(L, 1, "dnet"); - - if (!udata->interface || !udata->eth) - { - luaL_argerror(L, 1, "dnet is not valid opened ethernet interface"); - return 0; - } - - udata->eth = NULL; - ldnet_eth_close_cached(udata->interface); - free(udata->interface); - udata->interface = NULL; - return 0; -} - -static int l_dnet_send_ethernet(lua_State * L) -{ - l_dnet_udata *udata = (l_dnet_udata *) luaL_checkudata(L, 1, "dnet"); - - size_t packetsz = 0; - - const char *packet = luaL_checklstring(L, 2, &packetsz); - - if (!udata->interface || !udata->eth) - { - luaL_argerror(L, 1, "dnet is not valid opened ethernet interface"); - return 0; - } - eth_send(udata->eth, packet, packetsz); - return 0; -} - -static int l_dnet_open_ip(lua_State * L) -{ - l_dnet_udata *udata = (l_dnet_udata *) luaL_checkudata(L, 1, "dnet"); - - udata->sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); - - if (udata->sock == -1) { - lua_pushboolean(L, false); - lua_pushfstring(L, "Failed to open raw socket: %s (errno %d)", socket_strerror(socket_errno()), socket_errno()); - return 2; - } - - broadcast_socket(udata->sock); -#ifndef WIN32 - sethdrinclude(udata->sock); + nsock_pool nsp = new_pool(L); + if (o.scriptTrace()) + nsp_settrace(nsp, NSOCK_TRACE_LEVEL, o.getStartTime()); +#if HAVE_OPENSSL + /* Value speed over security in SSL connections. */ + nsp_ssl_init_max_speed(nsp); #endif - lua_pushboolean(L, true); - return 1; + return 0; } - -static int l_dnet_close_ip(lua_State * L) -{ - l_dnet_udata *udata = (l_dnet_udata *) luaL_checkudata(L, 1, "dnet"); - - if (udata->sock == -1) { - lua_pushboolean(L, false); - lua_pushstring(L, "Raw socket all ready closed"); - return 2; - } - - close(udata->sock); - - if (udata->eth && udata->interface) { - ldnet_eth_close_cached(udata->interface); - free(udata->interface); - udata->eth = NULL; - udata->interface = NULL; - } - - lua_pushboolean(L, true); - return 1; -} - -static int l_dnet_send_ip(lua_State * L) -{ - l_dnet_udata *udata = (l_dnet_udata *) luaL_checkudata(L, 1, "dnet"); - size_t packetsz = 0; - const char *packet = luaL_checklstring(L, 2, &packetsz); - char dev[16]; - int ret; - - if (udata->sock == -1) { - lua_pushboolean(L, false); - lua_pushstring(L, "Raw socket not open to send"); - return 2; - } - - if (packetsz < sizeof(struct ip)) { - lua_pushboolean(L, false); - lua_pushstring(L, "Won't send: IP packet too short"); - return 2; - } - - *dev = 0; - - if ((o.sendpref & PACKET_SEND_ETH)) { - struct route_nfo route; - struct sockaddr_storage srcss, dstss, *nexthop; - struct sockaddr_in *srcsin = (struct sockaddr_in *) &srcss; - struct sockaddr_in *dstsin = (struct sockaddr_in *) &dstss; - struct ip *ip = (struct ip *) packet; - u8 dstmac[6]; - eth_nfo eth; - - /* build sockaddr for target from user packet and determine route */ - memset(&dstss, 0, sizeof(dstss)); - dstsin->sin_family = AF_INET; - dstsin->sin_addr.s_addr = ip->ip_dst.s_addr; - - if (!nmap_route_dst(&dstss, &route)) - goto usesock; - - strncpy(dev, route.ii.devname, sizeof(dev)); - - if (route.ii.device_type != devt_ethernet) - goto usesock; - - /* above we fallback to using the raw socket if we can't find an (ethernet) - * route to the host. From here on out it's ethernet all the way. - */ - - /* build sockaddr for source from user packet to determine next hop mac */ - memset(&srcss, 0, sizeof(srcss)); - srcsin->sin_family = AF_INET; - srcsin->sin_addr.s_addr = ip->ip_src.s_addr; - - if (route.direct_connect) - nexthop = &dstss; - else - nexthop = &route.nexthop; - - if (!getNextHopMAC(route.ii.devfullname, route.ii.mac, &srcss, nexthop, dstmac)) { - lua_pushboolean(L, false); - lua_pushstring(L, "Failed to determine next hop MAC address"); - return 2; - } - - /* Use cached ethernet device, and use udata's eth and interface to keep - * track of if we're reusing the same device from the previous packet, and - * close the cached device if not. - */ - memset(ð, 0, sizeof(eth)); - memcpy(ð.srcmac, route.ii.mac, sizeof(eth.srcmac)); - memcpy(ð.dstmac, dstmac, sizeof(eth.dstmac)); - eth.ethsd = ldnet_eth_open_cached(dev); - if (!udata->eth) { - udata->eth = eth.ethsd; - udata->interface = strdup(route.ii.devname); - } else if (udata->eth != eth.ethsd) { - ldnet_eth_close_cached(udata->interface); - free(udata->interface); - udata->eth = eth.ethsd; - udata->interface = strdup(route.ii.devname); - } - ret = send_ip_packet(udata->sock, ð, (u8 *) packet, packetsz); - } else { -usesock: -#ifdef WIN32 - if (strlen(dev)) - win32_warn_raw_sockets(dev); -#endif - ret = send_ip_packet(udata->sock, NULL, (u8 *) packet, packetsz); - } - if (ret == -1) { - lua_pushboolean(L, false); - lua_pushfstring(L, "Error while sending: %s (errno %d)", socket_strerror(socket_errno()), socket_errno()); - return 2; - } - lua_pushboolean(L, true); - return 1; -} - diff --git a/nse_nsock.h b/nse_nsock.h index e8936809c..0193af1dc 100644 --- a/nse_nsock.h +++ b/nse_nsock.h @@ -1,12 +1,11 @@ #ifndef NMAP_LUA_NSOCK_H #define NMAP_LUA_NSOCK_H -int luaopen_nsock(lua_State *); -int l_nsock_new(lua_State *); -int l_nsock_sleep(lua_State *L); +#include "nse_main.h" -int l_dnet_new(lua_State *); -int l_dnet_get_interface_link(lua_State *); +LUALIB_API int luaopen_nsock (lua_State *); +LUALIB_API int l_nsock_new (lua_State *); +LUALIB_API int l_nsock_sleep (lua_State *); #define NSE_NSOCK_LOOP "NSOCK_LOOP" diff --git a/nse_utility.cc b/nse_utility.cc new file mode 100644 index 000000000..4d20e52d1 --- /dev/null +++ b/nse_utility.cc @@ -0,0 +1,180 @@ +#include + +#include "Target.h" +#include "portlist.h" + +#include "nse_main.h" +#include "nse_utility.h" + +/* 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. + */ +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)) + { + len++; + lua_pop(L, 1); + } + lua_pop(L, 1); + + return len; +} + +void setsfield (lua_State *L, int idx, const char *field, const char *what) +{ + lua_pushvalue(L, idx); + lua_pushstring(L, what); /* what can be NULL */ + lua_setfield(L, -2, field); + lua_pop(L, 1); +} + +void setnfield (lua_State *L, int idx, const char *field, lua_Number n) +{ + lua_pushvalue(L, idx); + lua_pushnumber(L, n); + lua_setfield(L, -2, field); + lua_pop(L, 1); +} + +void setbfield (lua_State *L, int idx, const char *field, int b) +{ + lua_pushvalue(L, idx); + lua_pushboolean(L, b); + lua_setfield(L, -2, field); + lua_pop(L, 1); +} + +int success (lua_State *L) +{ + lua_pushboolean(L, true); + return 1; +} + +int safe_error (lua_State *L, const char *message) +{ + lua_pushboolean(L, false); + lua_pushstring(L, message); + return 2; +} + +void weak_table (lua_State *L, int narr, int nrec, const char *mode) +{ + lua_createtable(L, narr, nrec); + lua_createtable(L, 0, 1); + lua_pushstring(L, mode); + lua_setfield(L, -2, "__mode"); + lua_setmetatable(L, -2); +} + +/* const char *check_target (lua_State *L, int idx) + * + * Check for a valid target specification at index idx. + * This function checks for a string at idx or a table containing + * the typical host table fields, 'ip' and 'targetname' in particular. + */ +void check_target (lua_State *L, int idx, const char **address, const char **targetname) +{ + if (lua_istable(L, idx)) { + lua_getfield(L, idx, "ip"); + *address = lua_tostring(L, -1); + lua_getfield(L, idx, "targetname"); + *targetname = lua_tostring(L, -1); + if (address == NULL && targetname == NULL) + luaL_argerror(L, idx, "host table lacks 'ip' or 'targetname' fields"); + *address = *address ? *address : *targetname; + lua_pop(L, 2); /* no point replacing idx, need 2 only have 1 */ + } else { + *address = *targetname = luaL_checkstring(L, idx); + } +} + +/* unsigned short check_port (lua_State *L, int idx) + * + * Check for a valid port specification at index idx. + */ +unsigned short check_port (lua_State *L, int idx, const char **protocol) +{ + unsigned short port; + + if (lua_istable(L, idx)) { + lua_getfield(L, idx, "number"); + if (!lua_isnumber(L, -1)) + luaL_argerror(L, idx, "port table lacks numeric 'number' field"); + port = (unsigned short) lua_tointeger(L, -1); + lua_getfield(L, idx, "protocol"); + *protocol = lua_tostring(L, -1); + lua_pop(L, 2); + } else { + port = (unsigned short) luaL_checkint(L, idx); + } + return port; +} + +/* Target *get_target (lua_State *L, int index) + * + * This function checks the value at index for a valid host table. It locates + * the associated Target (C++) class object associated with the host and + * returns it. If the Target is not being scanned then an error will be raised. + */ +Target *get_target (lua_State *L, int index) +{ + int top = lua_gettop(L); + Target *target; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "targetname"); + lua_getfield(L, index, "ip"); + if (!(lua_isstring(L, -2) || lua_isstring(L, -1))) + luaL_error(L, "host table does not have a 'ip' or 'targetname' field"); + if (lua_isstring(L, -2)) /* targetname */ + { + nse_gettarget(L, -2); /* use targetname */ + if (lua_islightuserdata(L, -1)) + goto done; + else + lua_pop(L, 1); + } + if (lua_isstring(L, -1)) /* ip */ + nse_gettarget(L, -1); /* use ip */ + if (!lua_islightuserdata(L, -1)) + luaL_argerror(L, 1, "host is not being processed right now"); +done: + target = (Target *) lua_touserdata(L, -1); + lua_settop(L, top); /* reset stack */ + return target; +} + +/* Target *get_port (lua_State *L, Target *target, Port *port, int index) + * + * This function checks the value at index for a valid port table. It locates + * the associated Port (C++) class object associated with the host and + * returns it. + */ +Port *get_port (lua_State *L, Target *target, Port *port, int index) +{ + Port *p = NULL; + int portno, protocol; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "number"); + if (!lua_isnumber(L, -1)) + luaL_error(L, "port 'number' field must be a number"); + lua_getfield(L, index, "protocol"); + if (!lua_isstring(L, -1)) + luaL_error(L, "port 'protocol' field must be a string"); + portno = (int) lua_tointeger(L, -2); + protocol = strcmp(lua_tostring(L, -1), "tcp") == 0 ? IPPROTO_TCP : + strcmp(lua_tostring(L, -1), "udp") == 0 ? IPPROTO_UDP : + strcmp(lua_tostring(L, -1), "sctp") == 0 ? IPPROTO_SCTP : + luaL_error(L, "port 'protocol' field must be \"udp\", \"sctp\" or \"tcp\""); + while ((p = target->ports.nextPort(p, port, protocol, PORT_UNKNOWN)) != NULL) + if (p->portno == portno) + break; + lua_pop(L, 2); + return p; +} diff --git a/nse_utility.h b/nse_utility.h new file mode 100644 index 000000000..1718b598f --- /dev/null +++ b/nse_utility.h @@ -0,0 +1,19 @@ +#ifndef NMAP_NSE_UTILITY_H +#define NMAP_NSE_UTILITY_H + +size_t table_length (lua_State *, int); +void setsfield (lua_State *, int, const char *, const char *); +void setnfield (lua_State *, int, const char *, lua_Number); +void setbfield (lua_State *, int, const char *, int); +void weak_table (lua_State *, int, int, const char *); + +int success (lua_State *); +int safe_error (lua_State *, const char *); + +void check_target (lua_State *, int, const char **, const char **); +unsigned short check_port (lua_State *, int, const char **); + +Target *get_target (lua_State *, int); +Port *get_port (lua_State *, Target *, Port *, int); + +#endif diff --git a/nselib/dhcp.lua b/nselib/dhcp.lua index b30ae09f5..3c9ba2322 100644 --- a/nselib/dhcp.lua +++ b/nselib/dhcp.lua @@ -35,12 +35,6 @@ request_types_str[6] = "DHCPNAK" request_types_str[7] = "DHCPRELEASE" request_types_str[8] = "DHCPINFORM" --- This pulls back 4 bytes in the packet that correspond to the transaction id. This should be randomly --- generated and different for every instance of a script (to prevent collisions) -pcap_callback = function(packetsz, layer2, layer3) - return string.sub(layer3, 33, 36) -end - ---Read an IP address or a list of IP addresses. Print an error if the length isn't a multiple of 4. -- --@param data The packet. @@ -369,9 +363,8 @@ local function dhcp_send(interface, host, packet, transaction_id) -- Create a pcap socket to listen for the response. I used to consider this a hack, but -- it really isn't -- it's kinda how this has to be done. local pcap = nmap.new_socket() - pcap:pcap_open(interface, 590, 0, pcap_callback, "udp port 68") + pcap:pcap_open(interface, 590, false, "udp port 68") pcap:set_timeout(5000) - pcap:pcap_register(transaction_id) stdnse.print_debug(1, "dhcp: Starting listener") -- Create the UDP socket (TODO: enable SO_BROADCAST if we need to) @@ -386,7 +379,12 @@ local function dhcp_send(interface, host, packet, transaction_id) socket:send(packet) -- Read the response - local status, err, _, data = pcap:pcap_receive() + local status, length, layer2, layer3 = pcap:pcap_receive(); + -- This pulls back 4 bytes in the packet that correspond to the transaction id. This should be randomly + -- generated and different for every instance of a script (to prevent collisions) + while status and layer3:sub(33, 36) ~= transaction_id do + status, length, layer2, layer3 = pcap:pcap_receive(); + end if(status == false) then stdnse.print_debug(1, "dhcp: Error calling pcap_receive(): %s", err) return false, "Error calling pcap_receive(): " .. err diff --git a/nselib/nmap.luadoc b/nselib/nmap.luadoc index 7925af3f7..b6dda959c 100644 --- a/nselib/nmap.luadoc +++ b/nselib/nmap.luadoc @@ -483,8 +483,7 @@ function receive_bytes(n) -- delimiter string (or matches the function passed in). This function -- continues to read from the network until the delimiter is found or the -- function times out. If data is read beyond the delimiter, that data is --- saved in a buffer for the next call to receive_buf. This --- buffer is cleared on subsequent calls to other Network I/O API functions. +-- saved in a buffer for the next call to receive_buf. -- -- The first argument may be either a pattern or a function. If a pattern, that -- pattern is used to separate the data. If a function, it must take exactly @@ -501,12 +500,7 @@ function receive_bytes(n) -- -- On success the function returns true along with the received data. On failure -- the function returns false or nil along with an --- error string. Possible error messages are the same as those that the other --- receive functions can return, with the addition of --- * "Error inside splitting-function": The first argument was a function which caused an error while being called. --- * "Error in string.find (nsockobj:receive_buf)!": A string was provided as the first argument, and string.find() yielded an error while being called. --- * "Expected either a function or a string!": The first argument was neither a function nor a string. --- * "Delimiter has negative size!": The returned start offset is greater than the end offset. +-- receive error string. This function may also throw errors for incorrect usage. -- @param delimiter A Lua pattern or a function with return values like those of -- string.find. -- @param keeppattern Whether to return the delimiter string with any returned @@ -565,37 +559,19 @@ function set_timeout(t) --- Opens a socket for raw packet capture. -- --- The callback function is a function that receives a packet with headers and --- computes a "packet hash", some value derived from the packet. For example, --- the callback function could extract the source IP address from a packet. The --- hash of each packet received is compared against all the strings registered --- with the pcap_register function. -- @param device The dnet-style interface name of the device you want to capture -- from. -- @param snaplen The length of each packet you want to capture (similar to the -- -s option to tcpdump) -- @param promisc Set to 1 if the interface should activate promiscuous mode, -- and 0 otherwise. --- @param test_function Callback function used to compute the packet hash. -- @param bpf A string describing a Berkeley Packet Filter expression (like -- those provided to tcpdump). --- @see new_socket, pcap_register, pcap_receive +-- @see new_socket, pcap_receive -- @usage -- local socket = nmap.new_socket() --- socket:pcap_open("eth0", 64, 0, callback, "tcp") -function pcap_open(device, snaplen, promisc, test_function, bpf) - ---- Starts listening for incoming packets. --- --- The provided packet_hash is a binary string which has to match --- the hash returned by the test_function parameter provided to --- pcap_open. If you want to receive all packets, just provide --- the empty string (""). There has to be a call to --- pcap_register before a call to pcap_receive. --- @param packet_hash A binary string that is compared against packet hashes. --- @see pcap_open, pcap_receive --- @usage socket:pcap_register("") -function pcap_register(packet_hash) +-- socket:pcap_open("eth0", 64, false, "tcp") +function pcap_open(device, snaplen, promisc, bpf) --- Receives a captured packet. -- @@ -609,7 +585,7 @@ function pcap_register(packet_hash) -- @return Data from the second OSI layer (e.g. ethernet headers). -- @return Data from the third OSI layer (e.g. IPv4 headers). -- @return Packet capture time, as floating point seconds since the epoch --- @see pcap_open, pcap_register +-- @see pcap_open -- @usage status, plen, l2_data, l3_data, time = socket:pcap_receive() function pcap_receive() diff --git a/nselib/ssh1.lua b/nselib/ssh1.lua index 784bd4497..f1dcee764 100644 --- a/nselib/ssh1.lua +++ b/nselib/ssh1.lua @@ -25,13 +25,14 @@ require "openssl" -- @return packet_length, packet_length or nil -- the return is similar to the lua function string:find() check_packet_length = function( buffer ) + if #buffer < 4 then return nil end local payload_length, packet_length, offset offset, payload_length = bin.unpack( ">I", buffer ) local padding = 8 - payload_length % 8 assert(payload_length) - packet_length = buffer:len() - if payload_length + 4 + padding > packet_length then return nil end - return packet_length, packet_length + local total = 4+payload_length+padding; + if total > #buffer then return nil end + return total, total; end --- Receives a complete SSH packet, even if fragmented @@ -43,7 +44,7 @@ end -- @return status True or false -- @return packet The packet received receive_ssh_packet = function( socket ) - local status, packet = socket:receive_buf(check_packet_length) + local status, packet = socket:receive_buf(check_packet_length, true) return status, packet end @@ -76,7 +77,7 @@ fetch_host_key = function(host, port) padding = 8 - packet_length % 8 offset = offset + padding - if padding + packet_length + 4 == data:len() then + if padding + packet_length + 4 == #data then -- seems to be a proper SSH1 packet local msg_code,host_key_bits,exp,mod,length,fp_input offset, msg_code = bin.unpack( ">c", data, offset ) diff --git a/nselib/ssh2.lua b/nselib/ssh2.lua index 802820a1c..06720910d 100644 --- a/nselib/ssh2.lua +++ b/nselib/ssh2.lua @@ -29,11 +29,12 @@ local SSH2 -- @return packet_length, packet_length or nil -- the return is similar to the lua function string:find() check_packet_length = function( buffer ) + if #buffer < 4 then return nil end -- not enough data in buffer for int local packet_length, offset offset, packet_length = bin.unpack( ">I", buffer ) assert(packet_length) if packet_length + 4 > buffer:len() then return nil end - return packet_length, packet_length + return packet_length+4, packet_length+4 end --- Receives a complete SSH packet, even if fragmented @@ -45,7 +46,7 @@ end -- @return status True or false -- @return packet The packet received transport.receive_packet = function( socket ) - local status, packet = socket:receive_buf(check_packet_length) + local status, packet = socket:receive_buf(check_packet_length, true) return status, packet end @@ -78,7 +79,9 @@ end -- @return Payload of the SSH-2 packet. transport.payload = function( packet ) local packet_length, padding_length, payload_length, payload, offset - offset, packet_length, padding_length = bin.unpack( ">Ic", packet ) + offset, packet_length = bin.unpack( ">I", packet ) + packet = packet:sub(offset); + offset, padding_length = bin.unpack( ">c", packet ) assert(packet_length and padding_length) payload_length = packet_length - padding_length - 1 if packet_length ~= packet:len() then diff --git a/scripts/firewalk.nse b/scripts/firewalk.nse index 6751e6fdd..7874d18e2 100644 --- a/scripts/firewalk.nse +++ b/scripts/firewalk.nse @@ -84,13 +84,6 @@ local checkpkt = function(reply, orig) return true end ---- pcap callback --- @return destination ip address, the ip protocol and icmp type -local callback = function(size, layer2, layer3) - local ip = packet.Packet:new(layer3, layer3:len()) - return bin.pack('ACC', ip.ip_bin_dst, ip.ip_p, ip.icmp_type) -end - --- set destination port and ip ttl to a generic tcp packet -- @param ip the ip object -- @param dport the layer 4 destination port @@ -274,6 +267,13 @@ local portrange = function(ports) return stdnse.strjoin(",", strrange) end +--- pcap check function +-- @return destination ip address, the ip protocol and icmp type +local function check (size, layer2, layer3) + local ip = packet.Packet:new(layer3, layer3:len()) + return bin.pack('ACC', ip.ip_bin_dst, ip.ip_p, ip.icmp_type) +end + -- main firewalking logic action = function(host) local sock = nmap.new_dnet() @@ -290,7 +290,7 @@ action = function(host) end -- filter for incoming icmp time exceeded replies - pcap:pcap_open(host.interface, 104, 0, callback, "icmp and dst host " .. saddr) + pcap:pcap_open(host.interface, 104, false, "icmp and dst host " .. saddr) try(sock:ip_open()) @@ -309,11 +309,14 @@ action = function(host) while retry < MAX_RETRIES do try(sock:ip_send(pkt.buf)) - pcap:pcap_register(bin.pack('ACC', pkt.ip_bin_src, packet.IPPROTO_ICMP, ICMP_TIME_EXCEEDED)) - local status, _, _, rep = pcap:pcap_receive() + local status, length, layer2, layer3 = pcap:pcap_receive(); + local test = bin.pack('ACC', pkt.ip_bin_src, packet.IPPROTO_ICMP, ICMP_TIME_EXCEEDED); + while status and test ~= check(length, layer2, layer3) do + status, length, layer2, layer3 = pcap:pcap_receive(); + end if status then - if checkpkt(rep, pkt) then + if checkpkt(layer3, pkt) then stdnse.print_debug(1, "Firewalk: discovered fwd port " .. port) table.insert(fwdports, port) break diff --git a/scripts/ipidseq.nse b/scripts/ipidseq.nse index 01d262a12..b8dba5cf4 100644 --- a/scripts/ipidseq.nse +++ b/scripts/ipidseq.nse @@ -33,9 +33,9 @@ require 'packet' local NUMPROBES = 6 ---- Pcap callback +--- Pcap check function -- @return Destination and source IP addresses and TCP ports -local callback = function(size, layer2, layer3) +local function check (size, layer2, layer3) local ip = packet.Packet:new(layer3, layer3:len()) return bin.pack('AA=S=S', ip.ip_bin_dst, ip.ip_bin_src, ip.tcp_dport, ip.tcp_sport) end @@ -222,7 +222,7 @@ action = function(host) try = nmap.new_try(function() sock:ip_close() end) - pcap:pcap_open(host.interface, 104, 0, callback, "tcp and dst host " .. saddr .. " and src host " .. daddr .. " and src port " .. port) + pcap:pcap_open(host.interface, 104, false, "tcp and dst host " .. saddr .. " and src host " .. daddr .. " and src port " .. port) pcap:set_timeout(host.times.timeout * 1000) @@ -231,12 +231,14 @@ action = function(host) while i <= NUMPROBES do try(sock:ip_send(tcp.buf)) - pcap:pcap_register(bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport)) - - local status, len, _, pkt = pcap:pcap_receive() + local status, len, layer2, layer3 = pcap:pcap_receive() + local test = bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport) + while status and test ~= check(len, layer2, layer3) do + status, len, layer2, layer3 = pcap:pcap_receive() + end if status then - table.insert(ipids, packet.u16(pkt, 4)) + table.insert(ipids, packet.u16(layer3, 4)) end updatepkt(tcp) diff --git a/scripts/path-mtu.nse b/scripts/path-mtu.nse index 8d8612eed..d8b713710 100644 --- a/scripts/path-mtu.nse +++ b/scripts/path-mtu.nse @@ -145,7 +145,7 @@ end -- This is all we can use since we can get various protocols back from -- different hosts -local callback = function(size, layer2, layer3) +local function check (size, layer2, layer3) local ip = packet.Packet:new(layer3, layer3:len()) return bin.pack('A', ip.ip_bin_dst) end @@ -306,7 +306,7 @@ action = function(host) try = nmap.new_try(function() sock:ip_close() end) - pcap:pcap_open(host.interface, 104, 0, callback, "dst host " .. saddr .. " and (icmp or (" .. proto .. " and src host " .. daddr .. " and src port " .. port .. "))") + pcap:pcap_open(host.interface, 104, false, "dst host " .. saddr .. " and (icmp or (" .. proto .. " and src host " .. daddr .. " and src port " .. port .. "))") -- Since we're sending potentially large amounts of data per packet, -- simply bump up the host's calculated timeout value. Most replies @@ -337,12 +337,14 @@ action = function(host) end end - pcap:pcap_register(bin.pack('A', pkt.ip_bin_src)) - - status, _, _, ip = pcap:pcap_receive() + local test = bin.pack('A', pkt.ip_bin_src) + local status, length, layer2, layer3 = pcap:pcap_receive() + while status and test ~= check(length, layer2, layer3) do + status, length, layer2, layer3 = pcap:pcap_receive() + end if status then - local t, v = checkpkt(ip, pkt) + local t, v = checkpkt(layer3, pkt) if t == "gotreply" then gotit = true break diff --git a/scripts/qscan.nse b/scripts/qscan.nse index a26585ade..f0d06f7ba 100644 --- a/scripts/qscan.nse +++ b/scripts/qscan.nse @@ -157,9 +157,9 @@ local tstat = function(n1, n2, u1, u2, v1, v2) return math.abs(u1 - u2) / math.sqrt(a * (b / dof)) end ---- Pcap callback +--- Pcap check -- @return Destination and source IP addresses and TCP ports -local callback = function(size, layer2, layer3) +local function check (size, layer2, layer3) local ip = packet.Packet:new(layer3, layer3:len()) return bin.pack('AA=S=S', ip.ip_bin_dst, ip.ip_bin_src, ip.tcp_dport, ip.tcp_sport) end @@ -418,7 +418,7 @@ action = function(host) local conf, delay, numtrips = try(getopts()) - pcap:pcap_open(host.interface, 104, 0, callback, "tcp and dst host " .. saddr .. " and src host " .. daddr) + pcap:pcap_open(host.interface, 104, false, "tcp and dst host " .. saddr .. " and src host " .. daddr) try(sock:ip_open()) @@ -452,15 +452,17 @@ action = function(host) stats[j].fam = -1 end - pcap:pcap_register(bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport)) - start = stdnse.clock_us() try(sock:ip_send(tcp.buf)) stats[j].sent = stats[j].sent + 1 - local status, len, _, pkt, stop = pcap:pcap_receive() + local test = bin.pack('AA=S=S', tcp.ip_bin_src, tcp.ip_bin_dst, tcp.tcp_sport, tcp.tcp_dport) + local status, length, layer2, layer3, stop = pcap:pcap_receive() + while status and test ~= check(length, layer2, layer3) do + status, length, layer2, layer3, stop = pcap:pcap_receive() + end if not stop then -- probably a timeout, just grab current time diff --git a/scripts/sniffer-detect.nse b/scripts/sniffer-detect.nse index 89d2ec596..94ed82944 100644 --- a/scripts/sniffer-detect.nse +++ b/scripts/sniffer-detect.nse @@ -24,14 +24,13 @@ hostrule = function(host) nmap.get_interface_link(host.interface) == 'ethernet' end -callback = function(packetsz, layer2, layer3) +local function check (packetsz, layer2, layer3) return string.sub(layer2, 0, 12) end do_test = function(dnet, pcap, host, test) - local _ - local status + local status, length, layer2, layer3 local i = 0 -- ARP requests are send with timeouts: 10ms, 40ms, 90ms @@ -41,15 +40,21 @@ do_test = function(dnet, pcap, host, test) -- flush buffers :), wait quite long. repeat pcap:set_timeout(100) - pcap:pcap_register(host.mac_addr_src .. host.mac_addr) - status ,_,_,_ = pcap:pcap_receive() + local test = host.mac_addr_src .. host.mac_addr + status, length, layer2, layer3 = pcap:pcap_receive() + while status and test ~= check(length, layer2, layer3) do + status, length, layer2, layer3 = pcap:pcap_receive() + end until status ~= true pcap:set_timeout(10 * i*i) - pcap:pcap_register(host.mac_addr_src .. host.mac_addr) dnet:ethernet_send(test) - status ,_,_,_ = pcap:pcap_receive() + local test = host.mac_addr_src .. host.mac_addr + status, length, layer2, layer3 = pcap:pcap_receive() + while status and test ~= check(length, layer2, layer3) do + status, length, layer2, layer3 = pcap:pcap_receive() + end if status == true then -- the basic idea, was to inform user about time, when we got packet -- so that 1 would mean (0-10ms), 2=(10-40ms) and 3=(40ms-90ms) @@ -79,7 +84,7 @@ action = function(host) } dnet:ethernet_open(host.interface) - pcap:pcap_open(host.interface, 64, 0, callback, "arp") + pcap:pcap_open(host.interface, 64, false, "arp") local test_static = host.mac_addr_src .. string.char(0x08,0x06, 0x00,0x01, 0x08,0x00, 0x06,0x04, 0x00,0x01) ..