mirror of
https://github.com/nmap/nmap.git
synced 2026-01-24 07:09:01 +00:00
This is a maintenance fix for the NSE Nsock library binding. The patch focuses on code correctness and simplicity. The patch also brings some initial updates with an eye towards the upcoming Lua 5.2 release. See [1] for a post concerning this branch. [1] http://seclists.org/nmap-dev/2010/q3/710
314 lines
8.5 KiB
C++
314 lines
8.5 KiB
C++
#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 <assert.h>
|
|
|
|
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;
|
|
}
|