1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00
Files
nmap/nse_dnet.cc
d33tah e3d1c178e3 Add newlines at the EOF in conformance to Daniel's coding standards
proposal. This only affects Nmap's root directory. We might also need to
modify the code which autogenerates Nmap's source code files such as IPv6
fingerprinting code.
2014-06-18 10:18:58 +00:00

370 lines
10 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"
#include "struct_ip.h"
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
#include <assert.h>
extern NmapOps o;
enum {
DNET_METATABLE = lua_upvalueindex(1),
DNET_ETHERNET_METATABLE = lua_upvalueindex(2),
CACHE_DNET_ETHERNET = lua_upvalueindex(3), /* Map of dnet userdata to ethernet device userdata */
CACHE_DEVICE_ETHERNET = lua_upvalueindex(4), /* Map of ethernet device string identifier to ethernet device userdata */
};
typedef struct nse_dnet_udata
{
eth_t *eth;
int sock; /* raw ip socket */
} nse_dnet_udata;
static int l_dnet_new (lua_State *L)
{
nse_dnet_udata *udata;
udata = (nse_dnet_udata *) lua_newuserdata(L, sizeof(nse_dnet_udata));
lua_pushvalue(L, DNET_METATABLE);
lua_setmetatable(L, -2);
udata->eth = NULL;
udata->sock = -1;
return 1;
}
static int l_dnet_get_interface_info (lua_State *L)
{
char ipstr[INET6_ADDRSTRLEN];
struct addr src, bcast;
struct interface_info *ii = getInterfaceByName(luaL_checkstring(L, 1),
o.af());
if (ii == NULL)
return nseU_safeerror(L, "failed to find interface");
memset(ipstr, 0, INET6_ADDRSTRLEN);
memset(&src, 0, sizeof(src));
memset(&bcast, 0, sizeof(bcast));
lua_newtable(L);
nseU_setsfield(L, -1, "device", ii->devfullname);
nseU_setsfield(L, -1, "shortname", ii->devname);
nseU_setnfield(L, -1, "netmask", ii->netmask_bits);
if (ii->addr.ss_family == AF_INET)
inet_ntop(AF_INET, &((struct sockaddr_in *)&ii->addr)->sin_addr,
ipstr, INET6_ADDRSTRLEN);
else if (ii->addr.ss_family == AF_INET6)
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&ii->addr)->sin6_addr,
ipstr, INET6_ADDRSTRLEN);
else
luaL_error(L, "unknown protocol");
nseU_setsfield(L, -1, "address", ipstr);
switch (ii->device_type) {
case devt_ethernet:
nseU_setsfield(L, -1, "link", "ethernet");
lua_pushlstring(L, (const char *) ii->mac, 6);
lua_setfield(L, -2, "mac");
/* calculate the broadcast address */
if (ii->addr.ss_family == AF_INET) {
src.addr_type = ADDR_TYPE_IP;
src.addr_bits = ii->netmask_bits;
src.addr_ip = ((struct sockaddr_in *)&ii->addr)->sin_addr.s_addr;
addr_bcast(&src, &bcast);
memset(ipstr, 0, INET6_ADDRSTRLEN);
if (addr_ntop(&bcast, ipstr, INET6_ADDRSTRLEN) != NULL)
nseU_setsfield(L, -1, "broadcast", ipstr);
}
break;
case devt_loopback:
nseU_setsfield(L, -1, "link", "loopback");
break;
case devt_p2p:
nseU_setsfield(L, -1, "link", "p2p");
break;
case devt_other:
default:
nseU_setsfield(L, -1, "link", "other");
}
nseU_setsfield(L, -1, "up", (ii->device_up ? "up" : "down"));
nseU_setnfield(L, -1, "mtu", ii->mtu);
return 1;
}
static int close_eth (lua_State *L)
{
eth_t **eth = (eth_t **) nseU_checkudata(L, 1, DNET_ETHERNET_METATABLE, "ethernet");
assert(*eth != NULL);
eth_close(*eth);
*eth = NULL;
return nseU_success(L);
}
static eth_t *open_eth_cached (lua_State *L, int dnet_index, const char *device)
{
eth_t **eth;
lua_getfield(L, CACHE_DEVICE_ETHERNET, device);
if (!lua_isuserdata(L, -1))
{
lua_pop(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);
lua_pushvalue(L, DNET_ETHERNET_METATABLE);
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
lua_setfield(L, CACHE_DEVICE_ETHERNET, device);
}
eth = (eth_t **) lua_touserdata(L, -1);
lua_pushvalue(L, dnet_index);
lua_pushvalue(L, -2); /* eth_t userdata */
lua_rawset(L, CACHE_DNET_ETHERNET);
lua_pop(L, 1); /* eth_t userdata */
return *eth;
}
static int ethernet_open (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) nseU_checkudata(L, 1, DNET_METATABLE, "dnet");
const char *interface_name = luaL_checkstring(L, 2);
struct interface_info *ii = getInterfaceByName(interface_name, o.af());
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);
return nseU_success(L);
}
static int ethernet_close (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) nseU_checkudata(L, 1, DNET_METATABLE, "dnet");
udata->eth = NULL;
lua_pushvalue(L, 1);
lua_pushnil(L);
lua_rawset(L, CACHE_DNET_ETHERNET);
return nseU_success(L);
}
static int ethernet_send (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) nseU_checkudata(L, 1, DNET_METATABLE, "dnet");
if (udata->eth == NULL)
return luaL_error(L, "dnet ethernet interface is not open");
eth_send(udata->eth, luaL_checkstring(L, 2), lua_rawlen(L, 2));
return nseU_success(L);
}
static int ip_open (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) nseU_checkudata(L, 1, DNET_METATABLE, "dnet");
udata->sock = nmap_raw_socket();
if (udata->sock == -1)
return luaL_error(L, "failed to open raw socket: %s (errno %d)",
socket_strerror(socket_errno()), socket_errno());
return nseU_success(L);
}
static int ip_close (lua_State *L)
{
nse_dnet_udata *udata = (nse_dnet_udata *) nseU_checkudata(L, 1, DNET_METATABLE, "dnet");
if (udata->sock == -1)
return nseU_safeerror(L, "raw socket already closed");
close(udata->sock);
udata->sock = -1;
return nseU_success(L);
}
static int ip_send (lua_State *L)
{
struct abstract_ip_hdr hdr;
struct sockaddr_storage dst;
nse_dnet_udata *udata = (nse_dnet_udata *) nseU_checkudata(L, 1, DNET_METATABLE, "dnet");
const char *packet;
const char *addr, *targetname;
size_t packetlen;
unsigned int payloadlen;
char dev[16];
int ret;
if (udata->sock == -1)
return luaL_error(L, "raw socket not open to send");
packet = luaL_checklstring(L, 2, &packetlen);
nseU_opttarget(L, 3, &addr, &targetname);
payloadlen = packetlen;
if (ip_get_data_any(packet, &payloadlen, &hdr) == NULL)
return luaL_error(L, "can't parse ip packet");
if (addr == NULL) {
/* Extract dst from packet contents. This is deprecated because it doesn't
work for link-local IPv6 addresses; there's no way to recover the
scope_id from the packet contents. */
dst = hdr.dst;
} else {
/* Resolve hostname or numeric IP. */
size_t dstlen;
int rc = resolve(addr, 0, &dst, &dstlen, AF_UNSPEC);
if (rc != 0)
return nseU_safeerror(L, gai_strerror(rc));
}
*dev = '\0';
if (o.sendpref & PACKET_SEND_ETH)
{
struct sockaddr_storage *nexthop;
struct route_nfo route;
u8 dstmac[6];
eth_nfo eth;
if (!nmap_route_dst(&dst, &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.
*/
if (route.direct_connect)
nexthop = &dst;
else
nexthop = &route.nexthop;
if (!getNextHopMAC(route.ii.devfullname, route.ii.mac, &hdr.src, 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(&eth, 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_getfield(L, 1, "ethernet_close");
lua_pushvalue(L, 1);
lua_call(L, 1, 0);
udata->eth = eth.ethsd = open_eth_cached(L, 1, route.ii.devname);
ret = send_ip_packet(udata->sock, &eth, &dst, (u8 *) packet, lua_rawlen(L, 2));
} else {
usesock:
#ifdef WIN32
if (strlen(dev) > 0)
win32_fatal_raw_sockets(dev);
#endif
ret = send_ip_packet(udata->sock, NULL, &dst, (u8 *) packet, lua_rawlen(L, 2));
}
if (ret == -1)
return nseU_safeerror(L, "error while sending: %s (errno %d)",
socket_strerror(socket_errno()), socket_errno());
return nseU_success(L);
}
static int gc (lua_State *L)
{
nseU_checkudata(L, 1, DNET_METATABLE, "dnet");
lua_getfield(L, 1, "ip_close");
lua_pushvalue(L, 1);
lua_call(L, 1, 0);
lua_getfield(L, 1, "ethernet_close");
lua_pushvalue(L, 1);
lua_call(L, 1, 0);
return 0;
}
LUALIB_API int luaopen_dnet (lua_State *L)
{
static const luaL_Reg l_dnet_metatable[] = {
{"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}
};
static const luaL_Reg l_dnet[] = {
{"new", l_dnet_new},
{"get_interface_info", l_dnet_get_interface_info},
{NULL, NULL}
};
int i;
int top = lua_gettop(L);
/* Create the library upvalues:
DNET_METATABLE = lua_upvalueindex(1),
DNET_ETHERNET_METATABLE = lua_upvalueindex(2),
CACHE_DNET_ETHERNET = lua_upvalueindex(3),
CACHE_DEVICE_ETHERNET = lua_upvalueindex(4),
*/
lua_newtable(L);
lua_newtable(L);
nseU_weaktable(L, 0, 0, "k"); /* dnet udata weak, eth device strong */
nseU_weaktable(L, 0, 0, "v"); /* eth_device weak */
luaL_newlibtable(L, l_dnet_metatable);
for (i = top+1; i < top+1+4; i++) lua_pushvalue(L, i);
luaL_setfuncs(L, l_dnet_metatable, 4);
lua_setfield(L, top+1, "__index");
lua_newtable(L);
lua_setfield(L, top+1, "__metatable");
for (i = top+1; i < top+1+4; i++) lua_pushvalue(L, i);
lua_pushcclosure(L, gc, 4);
lua_setfield(L, top+1, "__gc");
lua_newtable(L);
lua_setfield(L, top+2, "__metatable");
for (i = top+1; i < top+1+4; i++) lua_pushvalue(L, i);
lua_pushcclosure(L, close_eth, 4);
lua_setfield(L, top+2, "__gc");
luaL_newlibtable(L, l_dnet); /* external interface */
for (i = top+1; i < top+1+4; i++) lua_pushvalue(L, i);
luaL_setfuncs(L, l_dnet, 4);
return 1;
}