mirror of
https://github.com/nmap/nmap.git
synced 2025-12-15 04:09:01 +00:00
Now does most of it's work through Lua:
From Nmap-dev: "Many of the changes consist of changing how Nmap interfaces
with Lua that were sometimes awkward or inflexible. Most of the functions
have been made to be callable directly by Lua which offers many technical
advantages: stack management is alleviated, errors are handled cleanly and
are more descriptive, and there is increased reusability."
Additionally:
-- Moved all lua_State * symbols from "l" to "L". This is to maintain
consistency with other Lua libraries (convention) and to make our macros portable.
-- Moved file system manipulation over to nse_fs.cc (from nse_init.cc)
1422 lines
40 KiB
C++
1422 lines
40 KiB
C++
#include "nse_nsock.h"
|
|
#include "nse_macros.h"
|
|
#include "nse_string.h"
|
|
|
|
#include "nse_debug.h"
|
|
|
|
#include "nsock.h"
|
|
#include "nmap_error.h"
|
|
/* #include "osscan.h" */
|
|
#include "NmapOps.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
#include "utils.h"
|
|
#include "tcpip.h"
|
|
|
|
#if HAVE_OPENSSL
|
|
#include <openssl/ssl.h>
|
|
#endif
|
|
|
|
#define SCRIPT_ENGINE "SCRIPT ENGINE"
|
|
#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;
|
|
|
|
// defined in nse_main.cc but also declared here
|
|
// to keep the .h files clean
|
|
int process_waiting2running(lua_State *L, int resume_arguments);
|
|
|
|
static int l_nsock_connect(lua_State *L);
|
|
static int l_nsock_connect_queued(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 *lua_state);
|
|
void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
|
|
void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
|
|
void l_nsock_receive_buf_handler(nsock_pool nsp, nsock_event nse, void *lua_state);
|
|
|
|
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);
|
|
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 luaL_reg l_nsock [] = {
|
|
{"connect", l_nsock_connect_queued},
|
|
{"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}
|
|
};
|
|
|
|
static nsock_pool nsp;
|
|
|
|
/* There can't be more opened descriptors than max_descriptors_allowed
|
|
* (search below) If there are no more free slots, lua thread is
|
|
* freezed and saved to nsock_connect_queue. It's restored when when a
|
|
* descriptor becomes availble (after nsock_close). */
|
|
static int nsock_descriptors_used; /* nsock descriptors currently in use */
|
|
std::list<lua_State* > nsock_connect_queue; /* list of freezed threads waiting for desc */
|
|
|
|
/*
|
|
* 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 ncap_request{
|
|
int suspended; /* is the thread suspended? (lua_yield) */
|
|
lua_State *L; /* lua_State of current process
|
|
* or NULL if process isn't suspended */
|
|
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;
|
|
/*used for buffered reading */
|
|
int bufidx; /*index inside lua's registry */
|
|
int bufused;
|
|
|
|
struct ncap_socket *ncap_socket;
|
|
struct ncap_request *ncap_request;
|
|
int ncap_cback_ref;
|
|
};
|
|
|
|
void l_nsock_clear_buf(lua_State *L, l_nsock_udata* udata);
|
|
|
|
int l_nsock_open(lua_State *L) {
|
|
luaL_newmetatable(L, "nsock");
|
|
lua_createtable(L, 0, 20);
|
|
luaL_register(L, NULL, l_nsock);
|
|
lua_setfield(L, -2, "__index");
|
|
lua_pushcclosure(L, l_nsock_gc, 0);
|
|
lua_setfield(L, -2, "__gc");
|
|
lua_pushliteral(L, "");
|
|
lua_setfield(L, -2, "__metatable"); // protect metatable
|
|
lua_pop(L, 1);
|
|
|
|
nsp = nsp_new(NULL);
|
|
//nsp_settrace(nsp, o.debugging, o.getStartTime());
|
|
if (o.scriptTrace())
|
|
nsp_settrace(nsp, 5, o.getStartTime());
|
|
|
|
return NSOCK_WRAPPER_SUCCESS;
|
|
}
|
|
|
|
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);
|
|
udata->nsiod = NULL;
|
|
udata->ssl_session = NULL;
|
|
udata->timeout = DEFAULT_TIMEOUT;
|
|
udata->bufidx = LUA_NOREF;
|
|
udata->bufused= 0;
|
|
udata->ncap_socket = NULL;
|
|
udata->ncap_request = NULL;
|
|
udata->ncap_cback_ref = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
int l_nsock_loop(int tout) {
|
|
return nsock_loop(nsp, tout);
|
|
}
|
|
|
|
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_queued(lua_State *L) {
|
|
|
|
/* We allow at least 10 even max_parallelism is 1 because a single
|
|
script might open a few sockets at once and we don't want it to
|
|
deadlock when it tries to open the 2nd one. */
|
|
const int max_descriptors_allowed = MAX(o.max_parallelism, 10);
|
|
|
|
|
|
l_nsock_udata* udata = (l_nsock_udata*) luaL_checkudata(L, 1, "nsock");
|
|
|
|
if(udata->nsiod!=NULL){
|
|
/*should a script try to connect a socket, which is already connected
|
|
* we close the old connection, since it would have no access to it
|
|
* anyways
|
|
*/
|
|
if(o.scriptTrace()){
|
|
log_write(LOG_STDOUT,"%s: Trying to connect already connected socket - closing!\n",SCRIPT_ENGINE);
|
|
}
|
|
l_nsock_close(L);
|
|
lua_pop(L,1);
|
|
}
|
|
|
|
|
|
if(nsock_descriptors_used >= max_descriptors_allowed){
|
|
/* wait for free descriptor */
|
|
nsock_connect_queue.push_back(L);
|
|
|
|
/* I must know how many arguments are passed to nsock_connect.
|
|
* is there a better way? */
|
|
int arguments = 3;
|
|
const char *how = luaL_optstring(L, 4, "");
|
|
if(*how != '\0'){
|
|
arguments = 4;
|
|
int port = luaL_optinteger(L, 5, -1);
|
|
if(port!=-1)
|
|
arguments = 5;
|
|
}
|
|
|
|
if(o.scriptTrace())
|
|
log_write(LOG_STDOUT, "NSOCK_connect_queued: thread queued (%i args) %p\n", arguments, (void *)L);
|
|
|
|
return lua_yield(L, arguments);
|
|
}
|
|
return l_nsock_connect(L);
|
|
}
|
|
|
|
void l_nsock_connect_queued_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
|
lua_State *L = (lua_State*) lua_state;
|
|
/* well, this is really hackish, we can't just do process_waiting2running, because
|
|
* nsock_connect() can do lua_yield().
|
|
* Instead, we first execute nsock_connect, and if it returns lua_yield() (ie. -1)
|
|
* than we don't do process_waiting2running.
|
|
* So, in summary we can do two lua_yield() on thread (one in l_nsock_connect_queued,
|
|
* second in l_nsock_connect). But it works for me. */
|
|
int r = l_nsock_connect(L);
|
|
if(r != -1)
|
|
process_waiting2running((lua_State*) lua_state, 0);
|
|
}
|
|
|
|
|
|
static int l_nsock_connect(lua_State *L) {
|
|
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);
|
|
const char *how = luaL_optstring(L, 4, "tcp");
|
|
|
|
const char* error;
|
|
struct addrinfo *dest;
|
|
int error_id;
|
|
|
|
l_nsock_clear_buf(L, udata);
|
|
|
|
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;
|
|
}
|
|
|
|
udata->nsiod = nsi_new(nsp, NULL);
|
|
nsock_descriptors_used++;
|
|
|
|
switch (how[0]) {
|
|
case 't':
|
|
if (strcmp(how, "tcp")) goto error;
|
|
nsock_connect_tcp(nsp, udata->nsiod, l_nsock_connect_handler,
|
|
udata->timeout, L, dest->ai_addr, dest->ai_addrlen, port);
|
|
break;
|
|
case 'u':
|
|
if (strcmp(how, "udp")) goto error;
|
|
nsock_connect_udp(nsp, udata->nsiod, l_nsock_connect_handler,
|
|
L, dest->ai_addr, dest->ai_addrlen, port);
|
|
break;
|
|
case 's':
|
|
if (strcmp(how, "ssl")) goto error;
|
|
#ifdef HAVE_OPENSSL
|
|
nsock_connect_ssl(nsp, udata->nsiod, l_nsock_connect_handler,
|
|
udata->timeout, L, dest->ai_addr, dest->ai_addrlen, port,
|
|
udata->ssl_session);
|
|
break;
|
|
#else
|
|
lua_pushboolean(L, false);
|
|
lua_pushstring(L, "Sorry, you don't have OpenSSL\n");
|
|
return 2;
|
|
#endif
|
|
default:
|
|
goto error;
|
|
break;
|
|
}
|
|
|
|
freeaddrinfo(dest);
|
|
return lua_yield(L, 0);
|
|
|
|
error:
|
|
freeaddrinfo(dest);
|
|
luaL_argerror(L, 4, "invalid connection method");
|
|
return 0;
|
|
}
|
|
|
|
void l_nsock_connect_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
|
lua_State *L = (lua_State*) lua_state;
|
|
|
|
if(o.scriptTrace()) {
|
|
l_nsock_trace(nse_iod(nse), "CONNECT", TO);
|
|
}
|
|
|
|
if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) {
|
|
process_waiting2running((lua_State*) lua_state, 1);
|
|
} else {
|
|
process_waiting2running((lua_State*) lua_state, 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);
|
|
char* hexified;
|
|
|
|
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()) {
|
|
hexified = nse_hexify((const void*)string, string_len);
|
|
l_nsock_trace(udata->nsiod, hexified, TO);
|
|
free(hexified);
|
|
}
|
|
|
|
nsock_write(nsp, udata->nsiod, l_nsock_send_handler, udata->timeout, L, string, string_len);
|
|
return lua_yield(L, 0);
|
|
}
|
|
|
|
void l_nsock_send_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
|
lua_State *L = (lua_State*) lua_state;
|
|
|
|
if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) {
|
|
process_waiting2running((lua_State*) lua_state, 1);
|
|
} else {
|
|
process_waiting2running((lua_State*) lua_state, 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, L);
|
|
|
|
return lua_yield(L, 0);
|
|
}
|
|
|
|
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, L, nlines);
|
|
|
|
return lua_yield(L, 0);
|
|
}
|
|
|
|
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, L, nbytes);
|
|
|
|
return lua_yield(L, 0);
|
|
}
|
|
|
|
void l_nsock_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
|
lua_State *L = (lua_State*) lua_state;
|
|
char* rcvd_string;
|
|
int rcvd_len = 0;
|
|
char* hexified;
|
|
|
|
if(l_nsock_checkstatus(L, nse) == NSOCK_WRAPPER_SUCCESS) {
|
|
rcvd_string = nse_readbuf(nse, &rcvd_len);
|
|
|
|
if(o.scriptTrace()) {
|
|
hexified = nse_hexify((const void*) rcvd_string, (size_t) rcvd_len);
|
|
l_nsock_trace(nse_iod(nse), hexified, FROM);
|
|
free(hexified);
|
|
}
|
|
|
|
lua_pushlstring(L, rcvd_string, rcvd_len);
|
|
process_waiting2running((lua_State*) lua_state, 2);
|
|
} else {
|
|
process_waiting2running((lua_State*) lua_state, 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,
|
|
(protocol == IPPROTO_TCP)? "TCP" : "UDP",
|
|
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) {
|
|
// char* ipstring = (char*) safe_malloc(sizeof(char) * INET6_ADDRSTRLEN);
|
|
|
|
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!!
|
|
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);
|
|
nsock_descriptors_used--;
|
|
/* handle threads that are waiting for free sockets*/
|
|
if(nsock_connect_queue.size()){
|
|
lua_State *nl = nsock_connect_queue.front();
|
|
nsock_connect_queue.pop_front();
|
|
/* we can't restore lua thread here. instead create timer event with
|
|
* short timeout 0, and restore thread there*/
|
|
nsock_timer_create(nsp, l_nsock_connect_queued_handler, 0, (void*) nl);
|
|
}
|
|
|
|
|
|
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");
|
|
if(lua_gettop(L)==2){
|
|
/*we were called with 2 arguments only - push the default third one*/
|
|
lua_pushboolean(L,true);
|
|
}
|
|
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, L);
|
|
}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
|
|
*/
|
|
return lua_yield(L,3);
|
|
}
|
|
return 2;
|
|
}
|
|
/*yielding with 3 arguments since we need them when the callback arrives */
|
|
return lua_yield(L, 3);
|
|
}
|
|
|
|
void l_nsock_receive_buf_handler(nsock_pool nsp, nsock_event nse, void *lua_state) {
|
|
lua_State *L = (lua_State*) lua_state;
|
|
char* rcvd_string;
|
|
int rcvd_len = 0;
|
|
char* hexified;
|
|
int tmpidx;
|
|
l_nsock_udata* udata = (l_nsock_udata*) luaL_checkudata(L, 1, "nsock");
|
|
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()) {
|
|
hexified = nse_hexify((const void*) rcvd_string, (size_t) rcvd_len);
|
|
l_nsock_trace(nse_iod(nse), hexified, FROM);
|
|
free(hexified);
|
|
}
|
|
/* 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;
|
|
}
|
|
process_waiting2running((lua_State*) lua_state, 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;
|
|
process_waiting2running((lua_State*) lua_state, 2);
|
|
}else{ /*buffer should be empty */
|
|
process_waiting2running((lua_State*) lua_state, 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, L);
|
|
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;
|
|
}
|
|
|
|
/****************** 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->L = L;
|
|
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;
|
|
|
|
/* 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 lua_yield(L, 0);
|
|
}
|
|
|
|
/* (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);
|
|
// lua_pop(L, 1);/* just in case [nope, it's not needed]*/
|
|
|
|
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->L, 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*) 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->L;
|
|
|
|
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;
|
|
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 */
|
|
return process_waiting2running(L, 4);
|
|
else /* not suspended, just pass output */
|
|
return 4;
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************** 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}
|
|
};
|
|
|
|
int 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);
|
|
return NSOCK_WRAPPER_SUCCESS;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
int l_clock_ms(lua_State *L){
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
// no rounding error
|
|
// unless the number is greater than 100,000,000,000,000
|
|
double usec = 0.0; //MAX_INT*1000 = 4 294 967 296 000 <- miliseconds since epoch should fit
|
|
usec = tv.tv_sec*1000;
|
|
usec += (int)(tv.tv_usec/1000); // make sure it's integer.
|
|
|
|
lua_pushnumber(L, usec);
|
|
return 1;
|
|
}
|