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) ..