1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00
Files
nmap/nse_nsock.cc
fyodor 9ca4041ec1 Merged -r13793:HEAD from nmap-exp/dev/nmap branch now that we're opening up trunk development
again.  Here are the items which were merged:

------------------------------------------------------------------------
r13971 | jah | 2009-06-29 14:30:27 -0700 (Mon, 29 Jun 2009) | 2 lines

Improved a pattern for matching HTTP status-line, tidy away some variables and
fix a typo.
------------------------------------------------------------------------
r13967 | daniel | 2009-06-29 13:47:04 -0700 (Mon, 29 Jun 2009) | 5 lines

o Added a convenience top-level BSD makefile redirecting BSD make
  to GNU make on BSD systems.  This should help prevent bogus
  error reports when users run "make" instead of "gmake" on BSD
  systems. [Daniel Roethlisberger]

------------------------------------------------------------------------
r13965 | batrick | 2009-06-29 06:50:11 -0700 (Mon, 29 Jun 2009) | 14 lines

[NSE] The NSE Nsock Library binding no longer relies on garbage collection to
monitor the use of socket "slots". A thread (script) attempting to connect must
first obtain one of a limited number of available socket locks (usually 10 or
--max-parallelism). The binding would use garbage collection of sockets to
determine when a thread has finished using its allocated sockets. This is
unfortunately slow and requires us to constantly run the garbage collector to
cause timely reallocation. I have changed the binding to now regularly inspect
allocated sockets in the nsock_loop function. Available sockets slots are now
immediately reallocated and done with far less execution time.

See [1] for benchmarks and further explanation.

[1] http://seclists.org/nmap-dev/2009/q2/0624.html

------------------------------------------------------------------------
r13964 | batrick | 2009-06-29 06:37:49 -0700 (Mon, 29 Jun 2009) | 10 lines

[NSE] Fixed a rare (and usually undetectable) bug that can cause a SEGFAULT.
The NSE nsock library binding may attempt to push values on the stack of
a thread that ended due to an error. It is possible that the internal
Lua stack was completely full and any further pushed values would result
in a segmentation memory violation.

This bug is very hard to reproduce with a SEGFAULT but is usually visible
when Lua assertion checks are turned on. A socket handler routine must be
called AFTER a thread has ended in error.

------------------------------------------------------------------------
r13963 | batrick | 2009-06-29 05:51:20 -0700 (Mon, 29 Jun 2009) | 3 lines

Fixed some global scoped variables to be local. This caused a many scripts to
overwrite each others' sockets, options, etc.

------------------------------------------------------------------------
r13939 | joao | 2009-06-27 16:07:35 -0700 (Sat, 27 Jun 2009) | 2 lines

Fixed port rule to include ssl pop3 port, now that pop3.lua supports SSL connections in function capabilities

------------------------------------------------------------------------
r13938 | joao | 2009-06-27 16:06:28 -0700 (Sat, 27 Jun 2009) | 2 lines

Added transparent SSL support using comm.tryssl

------------------------------------------------------------------------
r13937 | joao | 2009-06-27 16:05:19 -0700 (Sat, 27 Jun 2009) | 2 lines

Added transparent SSL support using comm.tryssl

------------------------------------------------------------------------
r13936 | joao | 2009-06-27 16:03:50 -0700 (Sat, 27 Jun 2009) | 2 lines

Added SSL transparent support using comm.tryssl

------------------------------------------------------------------------
r13935 | joao | 2009-06-27 16:02:39 -0700 (Sat, 27 Jun 2009) | 2 lines

Added SSL transparent support using comm.tryssl

------------------------------------------------------------------------
r13934 | joao | 2009-06-27 16:01:38 -0700 (Sat, 27 Jun 2009) | 2 lines

Added SSL transparent support using comm.tryssl

------------------------------------------------------------------------
r13933 | joao | 2009-06-27 16:00:27 -0700 (Sat, 27 Jun 2009) | 2 lines

SSL transparent support using comm.tryssl

------------------------------------------------------------------------
r13932 | joao | 2009-06-27 15:19:58 -0700 (Sat, 27 Jun 2009) | 2 lines

Included transparent ssl support to function pop3.capabilities using comm.tryssl

------------------------------------------------------------------------
r13931 | joao | 2009-06-27 15:19:06 -0700 (Sat, 27 Jun 2009) | 3 lines

New version of comm.lua with function tryssl, that transparently adds support to ssl connections


------------------------------------------------------------------------
r13930 | joao | 2009-06-27 14:50:38 -0700 (Sat, 27 Jun 2009) | 6 lines

Fixed buffering problem exposed by david on nmap-dev list.
The problem was solved using a buffer to receive the data, making the script work fine in cases where the ssh packets are fragmented.

A very similar solution was applied to ssh1.lua.


------------------------------------------------------------------------
r13928 | batrick | 2009-06-27 04:43:12 -0700 (Sat, 27 Jun 2009) | 18 lines

[NSE] We now propogate a NSE initiated yield on a script through all user
coroutines so that NSE may resume control. Previously, scripts that would yield
in a child coroutine (e.g. a script's child coroutine generated by Lua's
coroutine.create function) would give control back to the script. A script
would yield in this way by making a blocking socket operation. NSE would be
unable to correctly resume child coroutine when the socket operation is
finished processing.

By yielding the chain of coroutines a script has operating, we allow to NSE to
handle the socket operation properly. NSE would then resume the entire chain so
execution may correctly resume at the coroutine which initiated the socket
operation. This restores the "illusion" that a script executes without
interruption.

See [1] for more information, further explanation, and some use cases.

[1] http://seclists.org/nmap-dev/2009/q2/0586.html

------------------------------------------------------------------------
r13817 | david | 2009-06-18 15:57:29 -0700 (Thu, 18 Jun 2009) | 3 lines

Improve an OS fingerprint with a model number and broader matching.
Based on a follow-up report from a submitter.

------------------------------------------------------------------------
r13814 | josh | 2009-06-17 21:34:15 -0700 (Wed, 17 Jun 2009) | 3 lines

[zenmap] Added support to zenmap for the new SCTP options: -PY, -sY and -sZ


------------------------------------------------------------------------
r13797 | ron | 2009-06-17 11:02:18 -0700 (Wed, 17 Jun 2009) | 1 line

Applied a patch from Mak Kolibabi that enhances the output of smb-enum-processes. The output is now modeled after the output of the 'ps' tool for higher verbosity levels.
------------------------------------------------------------------------
r13795 | david | 2009-06-17 09:05:21 -0700 (Wed, 17 Jun 2009) | 6 lines

The configure script now allows cross-compiling by assuming that
libpcap is recent enough. Previously it would quit because a test
program could not be run. libpcap will always be recent enough when
the included copy is used. The patch was contributed by Mike
Frysinger.
2009-06-29 23:48:19 +00:00

1904 lines
50 KiB
C++

extern "C"
{
#include "lua.h"
#include "lauxlib.h"
}
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sstream>
#include <iomanip>
#include "nse_nsock.h"
#include "nse_main.h"
#include "nsock.h"
#include "nmap_error.h"
#include "NmapOps.h"
#include "utils.h"
#include "tcpip.h"
#include "protocols.h"
#if HAVE_OPENSSL
# include <openssl/ssl.h>
#endif
#define SCRIPT_ENGINE "NSE"
#define NSOCK_WRAPPER "NSOCK WRAPPER"
#define NSOCK_WRAPPER_SUCCESS 0
#define NSOCK_WRAPPER_ERROR 2
#define NSOCK_WRAPPER_BUFFER_OK 1
#define NSOCK_WRAPPER_BUFFER_MOREREAD 2
#define FROM 1
#define TO 2
#define DEFAULT_TIMEOUT 30000
extern NmapOps o;
static int l_nsock_loop(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_ncap_open(lua_State * L);
static int l_nsock_ncap_close(lua_State * L);
static int l_nsock_ncap_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
{
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 */
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;
void *ssl_session;
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 */
struct ncap_socket *ncap_socket;
struct ncap_request *ncap_request;
int ncap_cback_ref;
};
/* 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)
{
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;
}
static 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);
}
static std::string hexify(const unsigned char *str, size_t len)
{
size_t num = 0;
std::ostringstream ret;
// If more than 95% of the chars are printable, we escape unprintable chars
for (size_t i = 0; i < len; i++)
if (isprint(str[i]))
num++;
if ((double) num / (double) len >= 0.95)
{
for (size_t i = 0; i < len; i++)
{
if (isprint(str[i]) || isspace(str[i]))
ret << str[i];
else
ret << std::setw(3) << "\\" << (unsigned int) (unsigned char) str[i];
}
return ret.str();
}
ret << std::setbase(16) << std::setfill('0');
for (size_t i = 0; i < len; i += 16)
{
ret << std::setw(8) << i << ": ";
for (size_t j = i; j < i + 16; j++)
if (j < len)
ret << std::setw(2) << (unsigned int) (unsigned char) str[j] << " ";
else
ret << " ";
for (size_t j = i; j < i + 16 && j < len; j++)
ret.put(isgraph(str[j]) ? (unsigned char) str[j] : ' ');
ret << std::endl;
}
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
* (open).
*
* THREAD_SOCKETS is a weak keyed table of <Thread, Socket Table> pairs.
* A socket table is a weak keyed table (socket keys with garbage values) of
* sockets the Thread has allocated but not necessarily open). You may
* test for an open socket by checking whether its nsiod field in the
* socket userdata structure is not NULL.
*
* CONNECT_WAITING is a weak keyed table of <Thread, Garbage Value> pairs.
* The table contains threads waiting to make a socket connection.
*/
#define MAX_PARALLELISM 10
#define THREAD_SOCKETS 1 /* <Thread, Table of Sockets (keys)> */
#define CONNECT_WAITING 2 /* Threads waiting to lock */
/* int socket_lock (lua_State *L)
*
* Arguments
* socket A socket to "lock".
*
* 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)
{
lua_settop(L, 1);
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, 1);
lua_pushboolean(L, true);
lua_rawset(L, -3);
} else if (table_length(L, 2) <= MAX(MAX_PARALLELISM, o.max_parallelism))
{
/* There is room for this thread to open sockets */
nse_base(L);
weak_table(L, 0, 0, "k"); /* weak socket references */
lua_pushvalue(L, 1); /* socket */
lua_pushboolean(L, true);
lua_rawset(L, -3); /* add to sockets table */
lua_rawset(L, 2); /* add new <Thread, Sockets Table> 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);
return nse_yield(L);
}
lua_pushboolean(L, true);
return 1;
}
static void socket_unlock(lua_State * L)
{
int top = lua_gettop(L);
lua_gc(L, LUA_GCSTOP, 0); /* don't collect threads during iteration */
lua_rawgeti(L, LUA_ENVIRONINDEX, THREAD_SOCKETS);
lua_pushnil(L);
while (lua_next(L, -2) != 0)
{
unsigned open = 0;
lua_pushnil(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)
open++;
}
if (open == 0) /* thread has no open sockets? */
{
lua_pushvalue(L, -2); /* thread key */
lua_pushnil(L);
lua_rawset(L, top+1); /* THREADS_SOCKETS */
lua_rawgeti(L, LUA_ENVIRONINDEX, CONNECT_WAITING);
lua_pushnil(L);
if (lua_next(L, -2) != 0)
{
lua_pop(L, 1); /* pop garbage boolean */
nse_restore(lua_tothread(L, -1), 0);
lua_pushnil(L);
lua_rawset(L, -3); /* remove thread from waiting */
}
lua_pop(L, 1); /* CONNECT_WAITING */
}
lua_pop(L, 1); /* pop sockets table */
}
lua_gc(L, LUA_GCRESTART, 0);
lua_settop(L, top);
}
void l_nsock_clear_buf(lua_State * L, l_nsock_udata * udata);
int luaopen_nsock(lua_State * L)
{
/* 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[] = {
{"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_ncap_open},
{"pcap_close", l_nsock_ncap_close},
{"pcap_register", l_nsock_ncap_register},
{"pcap_receive", l_nsock_pcap_receive},
// {"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");
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
l_dnet_open(L); /* open dnet metatable */
return 0;
}
int l_nsock_new(lua_State * L)
{
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->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;
}
static int l_nsock_loop(lua_State * L)
{
int tout = luaL_checkint(L, 1);
/* clean up old socket locks */
socket_unlock(L);
if (nsock_loop(nsp, tout) == NSOCK_LOOP_ERROR)
luaL_error(L, "a fatal error occurred in nsock_loop");
return 0;
}
int l_nsock_checkstatus(lua_State * L, nsock_event nse)
{
enum nse_status status = nse_status(nse);
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;
}
return -1;
}
static int l_nsock_connect(lua_State * L)
{
enum type {TCP, UDP, SSL};
static const char * const op[] = {"tcp", "udp", "ssl", NULL};
l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock");
const char *addr = luaL_checkstring(L, 2);
unsigned short port = (unsigned short) luaL_checkint(L, 3);
int what = luaL_checkoption(L, 4, "tcp", 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 (o.spoofsource)
{
struct sockaddr_storage ss;
size_t sslen;
o.SourceSockAddr(&ss, &sslen);
nsi_set_localaddr(udata->nsiod, &ss, sslen);
}
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, 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;
struct sockaddr local;
struct sockaddr remote;
char *ipstring_local = (char *) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN);
char *ipstring_remote = (char *) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN);
if (!nsi_is_pcap(nsiod))
{
status = nsi_getlastcommunicationinfo(nsiod, &protocol, &af,
&local, &remote, sizeof(sockaddr));
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);
free(ipstring_local);
free(ipstring_remote);
} 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;
char *ipstring_local = (char *) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN);
char *ipstring_remote = (char *) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN);
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);
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);
}
#ifdef HAVE_OPENSSL
if (udata->ssl_session)
SSL_SESSION_free((SSL_SESSION *) udata->ssl_session);
udata->ssl_session = NULL;
#endif
nsi_delete(udata->nsiod, NSOCK_PENDING_NOTIFY);
udata->nsiod = NULL;
lua_pushboolean(L, true);
return 1;
}
static int l_nsock_set_timeout(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;
return 0;
}
/* buffered I/O */
static int l_nsock_receive_buf(lua_State * L)
{
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;
}
/* yielding with 3 arguments since we need them when the callback arrives */
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 ***********************************************/
#ifdef WIN32
/* From tcpip.cc. Gets pcap device name from dnet name. */
bool DnetName2PcapName(const char *dnetdev, char *pcapdev, int pcapdevlen);
#endif
/* 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));
#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));
}
#else
strncpy(pcapdev, device, sizeof(pcapdev));
#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_ncap_open(lua_State * L)
{
l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock");
const char *device = luaL_checkstring(L, 2);
int snaplen = luaL_checkint(L, 3);
int promisc = luaL_checkint(L, 4);
luaL_checktype(L, 5, LUA_TFUNCTION); /* callback function that creates hash */
const char *bpf = luaL_checkstring(L, 6);
if (udata->nsiod || udata->ncap_request || udata->ncap_socket)
{
luaL_argerror(L, 1,
"Trying to open nsock-pcap, but this connection is already opened");
return 0;
}
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;
return 0;
}
/* (LUA) Close nsock-pcap socket.
* */
static int l_nsock_ncap_close(lua_State * L)
{
l_nsock_udata *udata = (l_nsock_udata *) luaL_checkudata(L, 1, "nsock");
struct ncap_socket *ns = udata->ncap_socket;
if (!udata->nsiod || !udata->ncap_socket)
{
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;
}
assert(ns->references > 0);
ns->references--;
if (ns->references == 0)
{
ncap_socket_map_del(ns->key);
if (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_ncap_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;
nse_readpcap(nse, &l2_data, &l2_len, &l3_data, &l3_len,
&packet_len, NULL);
char *packet = (char *) safe_malloc(l2_len + l3_len);
nr->r_layer2 = (unsigned char *) memcpy(&packet[0], l2_data, l2_len);
nr->r_layer3 = (unsigned char *) memcpy(&packet[l2_len], l3_data, l3_len);
nr->r_layer2_len = l2_len;
nr->r_layer3_len = l3_len;
nr->packetsz = packet_len;
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);
} else
{
lua_pushnil(L);
lua_pushstring(L, nr->r_status);
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 */
nse_restore(L, 4);
else /* not suspended, just pass output */
return 4;
return 0;
}
/****************** 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 luaL_reg l_dnet[] = {
{"ethernet_open", l_dnet_open_ethernet},
{"ethernet_close", l_dnet_close_ethernet},
{"ethernet_send", l_dnet_send_ethernet},
{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 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;
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;
}