1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-09 06:01:28 +00:00
Files
nmap/nse_nsock.cc
2007-09-08 05:56:15 +00:00

1387 lines
40 KiB
C++

#include "nse_nsock.h"
#include "nse_auxiliar.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, char* message, int direction);
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},
{"__gc",l_nsock_gc},
{"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) {
auxiliar_newclass(l, "nsock", l_nsock);
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));
auxiliar_setclass(l, "nsock", -1);
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);
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 != ""){
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*) auxiliar_checkclass(l, "nsock", 1);
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
luaL_argerror(l, 4, "Sorry, you don't have OpenSSL.");
return 0;
#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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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, 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, "SCRIPT ENGINE: %s %s:%d %s %s:%d | %s\n",
(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, "SCRIPT ENGINE: %s | %s\n",
(direction == TO)? ">" : "<",
message);
}
}
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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, 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*) auxiliar_checkclass(l, "nsock", 1);
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*) auxiliar_checkclass(l, "nsock", 1);
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, 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) {
auxiliar_newclass(l, "dnet", l_dnet);
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));
auxiliar_setclass(l, "dnet", -1);
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;
}
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*) auxiliar_checkclass(l, "dnet", 1);
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*) auxiliar_checkclass(l, "dnet", 1);
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*) auxiliar_checkclass(l, "dnet", 1);
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;
}