diff --exclude 'bpf_filt*' --exclude .svn -Nraupb nmap-4.20ALPHA11-NSE/nse_nsock.cc nmap-4.20ALPHA11-NSE-nsockbuf/nse_nsock.cc --- nmap-4.20ALPHA11-NSE/nse_nsock.cc 2006-11-11 15:12:41.000000000 +0100 +++ nmap-4.20ALPHA11-NSE-nsockbuf/nse_nsock.cc 2006-11-19 04:12:43.000000000 +0100 @@ -1,4 +1,5 @@ #ifndef NOLUA +/* 2006-11-18 nsock buffering by Marek Majkowski */ #include "nse_nsock.h" #include "nse_auxiliar.h" @@ -15,6 +16,8 @@ #include #include +#include + #if HAVE_OPENSSL #include @@ -44,10 +47,14 @@ static int l_nsock_receive_bytes(lua_Sta static int l_nsock_get_info(lua_State* l); static int l_nsock_close(lua_State* l); static int l_nsock_set_timeout(lua_State* l); +static int l_nsockbuf_receive_bytes(lua_State* l); +static int l_nsockbuf_receive_line(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_nsockbuf_receive_handler(nsock_pool nsp, nsock_event nse, void *lua_state); int l_nsock_checkstatus(lua_State* l, nsock_event nse); @@ -65,6 +72,8 @@ static luaL_reg l_nsock [] = { {"get_info", l_nsock_get_info}, {"close", l_nsock_close}, {"set_timeout", l_nsock_set_timeout}, + {"buf_receive_line", l_nsockbuf_receive_line}, + {"buf_receive_bytes", l_nsockbuf_receive_bytes}, {NULL, NULL} }; @@ -448,6 +457,8 @@ static int l_nsock_get_info(lua_State* l return 5; } +void nsockbuf_free(nsock_iod nsi); /* see nsockbuf docs in the bottom */ + static int l_nsock_close(lua_State* l) { l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); @@ -466,6 +477,8 @@ static int l_nsock_close(lua_State* l) { SSL_SESSION_free((SSL_SESSION*)udata->ssl_session); udata->ssl_session=NULL; #endif + /* Free nsockbuf internals, just in case nsockbuf was used. */ + nsockbuf_free(udata->nsiod); nsi_delete(udata->nsiod, NSOCK_PENDING_NOTIFY); @@ -484,4 +497,423 @@ static int l_nsock_set_timeout(lua_State return 0; } + + +/************* NSOCKBUF *****************/ +/*****************************************************************************/ +/* Using normal functions of nsock library you can't receive + exacly one line in result. This functions allow you to do this :) + They buffer results and return them in parts you requested. + + It is for you when you need to iterate through lines on + data received from nsock. + Or if you must receive exacly specified amount of data that can + be send by host in more than one packet. + + Using this features is of course slower than using normal nsock, but + this wrapper really makes programing nsock easier. + + TODO: This implementation uses 'memcpy'. It is possible to + create implementation without copying the same data many times. + Maybe I'll code it in the future. + TODO: Oh yes. This implementation uses . It's not + 'the fastest possible way'. Integrating this code to real nsock + will make this code even faster, because we could ommit searches + in . Question to Fyodor: maybe we should put this code into nsock? +*/ + +/* Request exacly one line from this NSI. Other parameters are like those in nsock */ +nsock_event_id nsockbuf_readline(nsock_pool nsp, nsock_iod nsi, + nsock_ev_handler handler, int timeout_msecs, + void *userdata); + +/* Request exacly NBYTES bytes from this socket. */ +nsock_event_id nsockbuf_readbytes(nsock_pool nsp, nsock_iod nsi, + nsock_ev_handler handler, int timeout_msecs, + void *userdata, int nbytes); + +/* Receive previusly requested data. Like nsock_readbuf(). + You should not modify received string. It is null terminated, + but it also can contain zero's inside. This string will be + freed automatically when you'll do nsockbuf_read* again. */ +char *nsockbuf_readbuf(nsock_event nse, int *nbytes); + +/* Is internal buffer of nsockbuf empty? Is there some data buffered? */ +bool nsockbuf_empty(nsock_iod nsi); + +/* Is this event fake? Sometimes you can receive event that looks + like NSE_TYPE_READ but in fact it's modified NSE_TYPE_TIMER. + This function allows you check if this event is real or faked. */ +bool nsockbuf_fake_read(nsock_event nse); + +/* Just free() all structures for this NSI. You'll loose buffered data. */ +void nsockbuf_free(nsock_iod nsi); + + +/*****************************************************************************/ +/* implementation of NSE related functions. + It's copied from Diman's non-buffered implementation */ +static int l_nsockbuf_receive_line(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + + if(udata->nsiod == NULL) { + lua_pushboolean(l, false); + lua_pushstring(l, "Trying to receive line through a closed socket\n"); + return 2; + } + + nsockbuf_readline(nsp, udata->nsiod, l_nsockbuf_receive_handler, udata->timeout, l); + return lua_yield(l, 0); +} + +static int l_nsockbuf_receive_bytes(lua_State* l) { + l_nsock_udata* udata = (l_nsock_udata*) auxiliar_checkclass(l, "nsock", 1); + int nbytes = (int) luaL_checknumber(l, 2); + + if(udata->nsiod == NULL) { + lua_pushboolean(l, false); + lua_pushstring(l, "Trying to receive bytes through a closed socket\n"); + return 2; + } + + nsockbuf_readbytes(nsp, udata->nsiod, l_nsockbuf_receive_handler, udata->timeout, l, nbytes); + return lua_yield(l, 0); +} + +void l_nsockbuf_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 = nsockbuf_readbuf(nse, &rcvd_len); + + if(o.scripttrace) { + hexified = l_nsock_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); + } +} + + + +/*****************************************************************************/ +/* Nsockbuf code */ + + +/***** NSOCKBUF internals */ +/* Every instance of this structure is linked with exacly one nsi. */ +struct nsockbuf_priv{ + int bytes_requested; // Have user requested bytes? + int lines_requested; // Have user requested line? + + char *buf; // receive buffer. + int buf_len; // how many bytes are inside this buffer + int buf_allocated; // how many bytes we allocated? + char *buf_free; // This is buffer that should be freed soon. It's given to user. + + // user's data + nsock_pool nsp; + nsock_iod nsi; + void *userdata; + nsock_ev_handler callback; + int timeout_msecs; + + bool fake_read; // Is current read faked? +}; +/* This mapps NSI with NSBP. We need to search for NSI sometimes. */ +std::map nsockbuf_map; +/***** /NSOCKBUF internals */ + + +/*****************************************************************************/ +/* nsockbuf functions */ + +// Free buffers for this nsi, I don't need it any more. +void nsockbuf_free(nsock_iod nsi){ + assert(nsi); + struct nsockbuf_priv *nsbp = nsockbuf_map[nsi]; + if(!nsbp) + return; + + if(o.debugging >= 3){ + if(nsbp->bytes_requested!=0 || nsbp->lines_requested!=0) + error("WARNING: nsockbuf: freeing nsi that was waiting for data."); + if(nsbp->buf_len) + error("WARNING: nsockbuf: wasted %i bytes", nsbp->buf_len); + } + + if(nsbp->buf_free) + free(nsbp->buf_free); + if(nsbp->buf) + free(nsbp->buf); + + nsockbuf_map.erase(nsi); + free(nsbp); +} + +// Is internal buffer of 'nsockbuf' empty? +bool nsockbuf_empty(nsock_iod nsi){ + struct nsockbuf_priv *nsbp = NULL; + if(nsi) + nsbp = nsockbuf_map[nsi]; + if(nsbp && nsbp->buf_len) + return(false); + return(true); +} + + +// helper function for nsockbuf_readline +static struct nsockbuf_priv* nsockbuf_create_nsbp(nsock_pool nsp, nsock_iod nsi){ + struct nsockbuf_priv *nsbp; + + nsbp = (struct nsockbuf_priv *)safe_zalloc(sizeof(struct nsockbuf_priv)); + nsbp->nsp = nsp; + nsbp->nsi = nsi; + nsockbuf_map[nsi] = nsbp; + return(nsbp); +} + +// helper function for nsockbuf_readline +static void nsockbuf_fill(struct nsockbuf_priv *nsbp, nsock_ev_handler handler, int timeout_msecs, void *userdata){ + nsbp->callback = handler; + nsbp->timeout_msecs = timeout_msecs; + nsbp->userdata = userdata; +} + +/* This handler is used to receive async nsock responses */ +void nsockbuf_handler(nsock_pool nsp, nsock_event nse, void *); + +// Request exacly one line. +nsock_event_id nsockbuf_readline(nsock_pool nsp, nsock_iod nsi, + nsock_ev_handler handler, int timeout_msecs, + void *userdata) +{ + nsock_event_id nse; + struct nsockbuf_priv *nsbp = nsockbuf_map[nsi]; + if(!nsbp) // create nsbp + nsbp = nsockbuf_create_nsbp(nsp, nsi); + assert(nsbp); + + if(nsbp->callback || nsbp->userdata || nsbp->bytes_requested || nsbp->lines_requested) + fatal("nsockbuf: Can't handle two requests for the same connection %i %i",nsbp->bytes_requested, nsbp->lines_requested); + + nsbp->lines_requested = 1; + nsockbuf_fill(nsbp, handler, timeout_msecs, userdata); + + if(nsbp->buf_len && memchr(nsbp->buf, '\n', nsbp->buf_len)) // already have valid data! + nse = nsock_timer_create(nsbp->nsp, nsockbuf_handler, 0, nsbp); + else + nse = nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, timeout_msecs, nsbp); + return(nse); +} + +// Request exact number of bytes. +nsock_event_id nsockbuf_readbytes(nsock_pool nsp, nsock_iod nsi, + nsock_ev_handler handler, int timeout_msecs, + void *userdata, int nbytes) +{ + nsock_event_id nse; + struct nsockbuf_priv *nsbp = nsockbuf_map[nsi]; + if(!nsbp) // create nsbp + nsbp = nsockbuf_create_nsbp(nsp, nsi); + assert(nsbp); + + if(nsbp->callback || nsbp->userdata || nsbp->bytes_requested || nsbp->lines_requested) + fatal("nsockbuf: Can't handle two requests for the same connection"); + + if(nbytes <= 0) + fatal("nsockbuf: you can't wait for 0 bytes"); + + nsbp->bytes_requested = nbytes; + nsockbuf_fill(nsbp, handler, timeout_msecs, userdata); + + if(nsbp->buf && (nsbp->buf_len >= nsbp->bytes_requested)) // already have data! + nse = nsock_timer_create(nsbp->nsp, nsockbuf_handler, 0, nsbp); + else + nse = nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, timeout_msecs, nsbp); + return(nse); +} + + +// Brutally change nsocks_event type and nsi. +// This is used to force change from TIMER to READ event. +void nsockbuf_event_change_type(nsock_event nse, enum nse_type type, nsock_iod nsi); + +/* handles nsocks async responses */ +void nsockbuf_handler(nsock_pool nsp, nsock_event nse, void *ud){ + enum nse_status status = nse_status(nse); + enum nse_type type = nse_type(nse); + struct nsockbuf_priv *nsbp = (struct nsockbuf_priv *) ud; + assert(nsbp); + assert(nsbp->callback); + assert(nsbp->bytes_requested || nsbp->lines_requested); + + + if(status == NSE_STATUS_SUCCESS && + (type == NSE_TYPE_READ || + type == NSE_TYPE_TIMER) ) + { + if(type == NSE_TYPE_READ){ + int rec_len; + char *rec = nse_readbuf(nse, &rec_len); + // copy data to buffer. TODO: optimize this!!!! + if(rec_len>0){ + if(!nsbp->buf){ + nsbp->buf = (char*)safe_malloc(rec_len); + nsbp->buf_allocated = rec_len; + }else if((nsbp->buf_allocated - (nsbp->buf_len+rec_len)) < 0){// not enough space + nsbp->buf = (char*)safe_realloc(nsbp->buf, nsbp->buf_len + rec_len); + nsbp->buf_allocated = nsbp->buf_len + rec_len; + } + memcpy(&nsbp->buf[nsbp->buf_len], rec, rec_len); + nsbp->buf_len += rec_len; + } + assert(nsbp->buf_allocated >= nsbp->buf_len); + // data okay? do callback + if((nsbp->bytes_requested && + nsbp->buf_len>=nsbp->bytes_requested) || + (nsbp->lines_requested && + memchr(nsbp->buf, '\n', nsbp->buf_len))) + nsbp->callback(nsp, nse, nsbp->userdata); // inform user that data has arrived + else //data not okay? request more + nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, nsbp->timeout_msecs, nsbp); + return; + }else{// TIMER + // Ugly hack :) Pretend this was READ event, not TIMER + nsockbuf_event_change_type(nse, NSE_TYPE_READ, nsbp->nsi); + nsbp->fake_read = 1; + nsbp->callback(nsp, nse, nsbp->userdata); + nsbp->fake_read = 0; + return; + } + }else // forward 'bad' events to user + nsbp->callback(nsp, nse, nsbp->userdata); +} + +char *nsockbuf_getbytes(struct nsockbuf_priv *nsbp, int* rec_len); +char *nsockbuf_getlines(struct nsockbuf_priv *nsbp, int* rec_len); + +// Receive what you requested. +char *nsockbuf_readbuf(nsock_event nse, int *nbytes){ + nsock_iod nsi = nse_iod(nse); + + assert(nsi); + + struct nsockbuf_priv *nsbp; + nsbp = nsockbuf_map[nsi]; + assert(nsbp); + assert(nbytes); + + if(nsbp->bytes_requested) + return(nsockbuf_getbytes(nsbp, nbytes)); + else if(nsbp->lines_requested) + return(nsockbuf_getlines(nsbp, nbytes)); + else + fatal("nsockbuf: you haven't requested anything from this socket!"); + return(NULL); +} + + +// helper for nsockbuf_readbuf +char *nsockbuf_getbytes(struct nsockbuf_priv *nsbp, int* rec_len){ + assert(nsbp->bytes_requested); + + if(nsbp->buf_free){ + free(nsbp->buf_free); + nsbp->buf_free = NULL; + } + if(nsbp->buf && nsbp->buf_len>=nsbp->bytes_requested){ + int out_len = nsbp->bytes_requested; + char *out = (char*)safe_malloc(out_len+1); + memcpy(out, nsbp->buf, out_len); + out[out_len]='\0'; // it's much nicer to get real null-terminated string + + memmove(nsbp->buf, &nsbp->buf[out_len], nsbp->buf_len - out_len); + nsbp->buf_len -= out_len; + + nsbp->buf_free = out; + *rec_len = out_len; + + nsbp->bytes_requested = 0; + nsbp->userdata = NULL; + nsbp->callback = NULL; + return(out); + } + + // nsockbuf_getbytes called but no data availble + *rec_len = 0; + return(""); +} + +// helper for nsockbuf_readbuf +char *nsockbuf_getlines(struct nsockbuf_priv *nsbp, int* rec_len){ + assert(nsbp->lines_requested); + + if(nsbp->buf_free){ + free(nsbp->buf_free); + nsbp->buf_free = NULL; + } + char *p; + if( nsbp->buf && (p=(char*)memchr(nsbp->buf, '\n', nsbp->buf_len)) ){ + int out_len = (p - nsbp->buf)+1; // after '\n' + char *out = (char*)safe_malloc(out_len+1); + memcpy(out, nsbp->buf, out_len); + out[out_len]='\0'; // it's much nicer to get real null-terminated string + + memmove(nsbp->buf, &nsbp->buf[out_len], nsbp->buf_len - out_len); + nsbp->buf_len -= out_len; + + nsbp->buf_free = out; + *rec_len = out_len; + + nsbp->lines_requested = 0; + nsbp->userdata = NULL; + nsbp->callback = NULL; + return(out); + } + // nsockbuf_getlines called but no data availble + *rec_len = 0; + return(""); +} + +// Your handler can receive real event or just faked. You should distinguish them +// when you update your timers :) +bool nsockbuf_fake_read(nsock_event nse){ + if(!nse) + return(false); + + nsock_iod nsi = nse_iod(nse); + if(!nsi) + return(false); + + struct nsockbuf_priv *nsbp; + nsbp = nsockbuf_map[nsi]; + if(!nsbp) + return(false); + + return(nsbp->fake_read); +} + + + +// Brutally change nsocks_event type and nsi. +// This is used to force change from TIMER to READ event. +#include "nsock/src/nsock_internal.h" +void nsockbuf_event_change_type(nsock_event nse, enum nse_type type, nsock_iod nsi){ + struct msevent *mse = (struct msevent*)nse; + mse->type = type; + mse->iod = (msiod*) nsi; +} + + + + #endif /* NOLUA */