diff -Nraupb -I ' \$Id: ' nmap-org/global_structures.h nmap-new/global_structures.h --- nmap-org/global_structures.h 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/global_structures.h 2006-07-26 02:27:49.000000000 +0200 @@ -234,6 +234,6 @@ struct scan_lists { int prot_count; }; -typedef enum { STYPE_UNKNOWN, HOST_DISCOVERY, ACK_SCAN, SYN_SCAN, FIN_SCAN, XMAS_SCAN, UDP_SCAN, CONNECT_SCAN, NULL_SCAN, WINDOW_SCAN, RPC_SCAN, MAIMON_SCAN, IPPROT_SCAN, PING_SCAN, PING_SCAN_ARP, IDLE_SCAN, BOUNCE_SCAN, SERVICE_SCAN, OS_SCAN} stype; +typedef enum { STYPE_UNKNOWN, HOST_DISCOVERY, ACK_SCAN, SYN_SCAN, FIN_SCAN, XMAS_SCAN, UDP_SCAN, CONNECT_SCAN, NULL_SCAN, WINDOW_SCAN, RPC_SCAN, MAIMON_SCAN, IPPROT_SCAN, PING_SCAN, PING_SCAN_ARP, IDLE_SCAN, SERVICE_SCAN, OS_SCAN, GSE_SCAN} stype; #endif /*GLOBAL_STRUCTURES_H */ diff -Nraupb -I ' \$Id: ' nmap-org/gse.cc nmap-new/gse.cc --- nmap-org/gse.cc 1970-01-01 01:00:00.000000000 +0100 +++ nmap-new/gse.cc 2006-07-26 02:27:49.000000000 +0200 @@ -0,0 +1,2398 @@ + +/*************************************************************************** + * gse.cc -- General Scanning Engine, asynchronous proxy chaining * + * * + ***************************************************************************/ + +/* $Id: $ */ + +#include +// we need definitions of few raw structures, sorry. +#include "nsock/src/nsock_internal.h" +#include "nmap.h" +#include "NmapOps.h" +#include "tcpip.h" +#include "global_structures.h" +#include "gse.h" +#include + +void initialize_timeout_info(struct timeout_info *to); +void adjust_timeouts3(long delta, struct timeout_info *to); // from timing.h +char *cstring_unescape(char *str, unsigned int *len); // from utils.h + +extern NmapOps o; // used only in HOST_resolve. and for o.debugging/o.verbose + + +using namespace std; + +/*************************************************************************** + Local definitions + ***************************************************************************/ + + +/************* PCRE *****************/ +/* simple helper for PCRE library */ +class Pcre{ + pcre *regex_compiled; + pcre_extra *regex_extra; + int ovector[100]; + char *resu[50]; + int resu_pos; + int offset; + int rc; + +public: + /* Constructor. + * re - your regular expression + * re_opts - pcre options like PCRE_MULTILINE or PCRE_CASELESS. see man 3 pcreapi for more info */ + Pcre(char *re, int re_opts); + + /* Match line against regular expression. + * text - string queried + * text_len - string length + * Returns: negative if not mached, 0 or more if matched successfully + **/ + int match(char *text, int text_len); + + /* If you're using PCRE_MULTILINE this command will move to next line. + * Next execution of 'match()' will give results of next line. */ + void move(); + + /* This should be executed before sequence of 'match();move()' functions. + * It resets offset of queried line. */ + void init(); + + /* If 'match()' was successful you can get substrings using these commands. + * text - quieried string + * i - number of substring (beginning from 1)*/ + char *get_dup(char *text, int i); // you'll have to 'free' this + char *get(char *text, int i); // don't 'free' result. It'll be done automatically. + + ~Pcre(); +private: + void flush(); +}; + + + +/************* NSOCKBUF *****************/ +/* Using normal functions of nsock library you can't receive + exacly one line in result. This functions allow you to do this :) */ + +// request one line. +nsock_event_id nsockbuf_readline(nsock_pool nsp, nsock_iod nsi, + nsock_ev_handler handler, int timeout_msecs, + void *userdata); +// request exact number of bytes in result +nsock_event_id nsockbuf_readbytes(nsock_pool nsp, nsock_iod nsi, + nsock_ev_handler handler, int timeout_msecs, + void *userdata, int nbytes); +// receive what you requested +char *nsockbuf_readbuf(nsock_event nse, int *nbytes); + +// is internal buffer of 'nsockbuf' empty? +bool nsockbuf_empty(nsock_iod nsi); +// free buffers for this nsi, I don't need it any more. +void nsockbuf_free(nsock_iod nsi); + +// how many miliseconds were from setting this event till receiving it? +long nsockbuf_geteventtime(nsock_event vnse); + + +// remove data that is waiting +void nsockbuf_flush(nsock_iod nsi); + + +/************* GSEPROXYINTERFACE *****************/ +// generic nsock handler for scanning engines. +void gse_generic_handler(nsock_pool nsp, nsock_event nse, void *ud); + +// temporary structure used in 'getportstatus' +struct gse_gps_tmp{ + HOST *host; + u16 port; + u8 protocol; + enum gse_reasons reason; + char reason_txt[128]; +}; + + +class GSEProxy_Interface{ +protected: + // TARGET'S DATA + u8 target_proto; // IPPROTO_TCP or IPPROTO_UDP + u16 target_port; // port number + HOST *target_host; // host address + + enum gse_reasons reason; // is port opened or closed? + enum gse_timeout_owner{GSE_TARGET,GSE_PROXY}; +private: + char reason_txt[128]; // description (for errors) + + enum gse_timeout_owner last_gto; // during last 'read' which timer we used; + +public: + CHAIN *chain; // pointer to chain. It's the same as gsc->chain[chainpos] + + class GSEConnection *gsc; // my parent's connection + +public: + /* Our state. */ + enum gse_states state; // current state + + /* Get static string with target name and port number */ + char *tostr(); + + +public: + /* THIS ONE MUST BE OVERLOADED */ + virtual void handler(enum gse_events event,nsock_event nse); + + +public: + /* Ask for new port. First it sets target_*, than it runs handler */ + void querynewport(HOST *host, u16 port, u8 proto); + /* Get state of last scanned port. It runs handler also. */ + virtual struct gse_gps_tmp getportstatus(); + + + void GSEProxy_Int_constr(GSEConnection *ngsc, int nchainpos); + + void set_reason_txt(const char *fmt, ...); + + long get_timeout(); + long get_timeout(enum gse_timeout_owner gto); // yep. select whether we should use PROXY or TARGET'S timer + + struct timeout_info *get_timeout_info(); // get timeout_info structure that should be updated; + + + // virtual destructor. + virtual ~GSEProxy_Interface(); +}; + + +/************* ENGINES *****************/ +void *GSEProxy_NSock_new(); +void *GSEProxy_HttpConnect_new(); +void *GSEProxy_HttpGet_new(); +void *GSEProxy_Socks4a_new(); +void *GSEProxy_FtpBounce_new(); +void *GSEProxy_Imap_new(); + + + +/*************************************************************************** + The code + ***************************************************************************/ + +void gse_generic_handler(nsock_pool nsp, nsock_event nse, void *ud){ + enum nse_status status = nse_status(nse); + enum nse_type type = nse_type(nse); + + GSEProxy_Interface *gpi = (GSEProxy_Interface*) ud; + assert(gpi); + + // run handler if needed. + switch(status){ + case NSE_STATUS_SUCCESS: + switch(type){ + case NSE_TYPE_CONNECT: + adjust_timeouts3(nsockbuf_geteventtime(nse), gpi->get_timeout_info()); + gpi->handler(GSEE_CONNECT, nse); + break; + case NSE_TYPE_READ: + /* We change type from TIMER to READ (in nsockbuf.cc) + nsi==NULL if this event is fake. + Of course we want to count time only for real READ events. + (in other case timer=0 or 1) */ + if(nse_iod(nse)) + adjust_timeouts3(nsockbuf_geteventtime(nse), gpi->get_timeout_info()); + gpi->handler(GSEE_READ, nse); + break; + case NSE_TYPE_WRITE: + break; + case NSE_TYPE_TIMER: // timer is executed on the start. to trigger first execution. + break; + default: + assert(0); + } + break; + case NSE_STATUS_EOF: + gpi->handler(GSEE_EOF, nse); + break; + case NSE_STATUS_ERROR: + switch(type){ + case NSE_TYPE_READ: + case NSE_TYPE_CONNECT: + gpi->handler(GSEE_EOF, nse); + break; + default: + error("WARN: generic_handler: BAD CALLBACK type %s with status %s on %s (%s %s)", nse_type2str(type), nse_status2str(status), + gpi->tostr(), nse_type2str(type), strerror(socket_errno())); + }; + break; + case NSE_STATUS_TIMEOUT: + gpi->handler(GSEE_TIMEOUT, nse); + break; + case NSE_STATUS_KILL: + case NSE_STATUS_CANCELLED: + break; + default: + assert(0); + } + + + /* yes, I know that this is not very nice way of getting + parent structures, but that's the only way */ + + assert(gpi && gpi->gsc); + int conn_off = gpi->gsc->gss_pool_off; + GSEScanner *gss = (GSEScanner*)nsp_getud(nsp); + + + bool sth_changed=true; + while(sth_changed && gss->conn_exists(conn_off)){ // in case it was deleted + switch(gss->conn_get_state(conn_off)){ + case GSS_DEAD: + gss->conn_remove(conn_off); + break; + case GSS_WAITING_FOR_INPUT: + gss->conn_give_input(conn_off); + break; + case GSS_WAITING_FOR_OUTPUT: + gss->conn_read_output(conn_off); + break; + case GSS_ALIVE: // connected!!! + gss->conn_alive(conn_off); + break; + default: + sth_changed = false; + } // switch + } // while +} + + + +char *gse_reason_tostr(enum gse_reasons reason){ + switch(reason){ + case GSER_OPENED: + return("opened"); + case GSER_CLOSED: + return("closed"); + case GSER_FILTERED: + return("filtered"); + case GSER_GW_ERROR: + return("gateway_error"); + case GSER_GW_FILTERED: + return("gateway_filtered"); + case GSER_CLOSED_FILTERED: + return("closed/filtered"); + case GSER_NONE: + return("none"); + } + return("unknown"); +} + + +// proto://user:pass@host:port|proto://user:pass@host:port +// proto:user:pass@host:port +// proto://host:port +// proto:host:port +/* + For this moment you can't use ':' '@' or ' ' as characters + in password, hostname or username. + +*/ +CHAIN *CHAIN_create(int *chain_len, char *text){ + Pcre *chain_re = new Pcre("^([^:@]+):(//)?(([^:@]*):([^:@]*)@)?([^: @]+):([0-9]+)$", + PCRE_CASELESS|PCRE_MULTILINE); + + /* Count and split lines */ + char *z, *b = text; + int lines_number = 1; + + while( (z=strchr(b,' ')) ){ + *z = '\n'; + b = z+1; + lines_number++; + } + + + /* Create chain */ + CHAIN *chain = (CHAIN*)safe_zalloc(sizeof(CHAIN)*(lines_number+1)); + initialize_timeout_info(&chain[0].to); + chain[0].engine = GSEENG_NSOCK; + + + /* regexp this! */ + chain_re->init(); + int ln; + for(ln=0; lnmatch(text, strlen(text)); + if(rc == 0) // not matched + break; + chain_re->move(); + + /* parse results */ + if(rc != 8) + fatal("Is your specification of scanning engine really in format: 'type://[user:pass@]host:port' ?"); + engine = chain_re->get(text, 1); + user = chain_re->get(text, 4); + pass = chain_re->get(text, 5); + host = chain_re->get(text, 6); + port = chain_re->get(text, 7); + + /* find engine by name */ + //foreach engine + char **cc; + enum gse_engines e = GSEENG_UNINITIALISED; + for(int i=0; i0 && pass){ // password can be length=0 + chain[ln+1].user = cstring_unescape(strdup(user),&a); + chain[ln+1].pass = cstring_unescape(strdup(pass),&a); + } + chain[ln+1].px_host = HOST_new(cstring_unescape(host,&a)); + chain[ln+1].px_port = atoi(port); + initialize_timeout_info(&chain[ln+1].to); + + //error checks again: + // warn if we'd have to resolve hostname locally + HOST_resolve_simple(chain[ln+1].px_host); + // no ip yet and no dns resolution step above. + if(! chain[ln+1].px_host->ip && ln!=0 && + !(gsee_definitions[chain[ln].engine].flags & GSEF_DNS)){ + error("WARNING: Host '%s' will be resolved on LOCAL side, not remote", HOST_tostr(chain[ln+1].px_host)); + } + } + + if(ln != lines_number) + fatal("Sorry, I can't understand all of your parameters in scanning engine query"); + + delete chain_re; + *chain_len = lines_number+1; + return(chain); +} + +void CHAIN_delete(CHAIN *chain, int chain_len){ + for(int i=0; iengine].names[0], + HOST_tostr(chain->px_host), chain->px_port); + return(a); +} + + + +/*************************************************************************/ +void GSEConnection::build_hop(int i){ + + GSEProxy_Interface *iface = NULL; + int eng = chain[i].engine; + + assert(eng < gsee_definitions_len); + assert(gsee_definitions[eng].engine == eng); + + if(gsee_definitions[eng].flags & GSEF_FIRST && i!=0) + fatal("ERROR: %i scanning engine could be used only as first in the chain!", i+1); + if(gsee_definitions[eng].flags & GSEF_LAST && i!=proxy_pool_len-1) + fatal("ERROR: %i scanning engine could be used only as last in the chain!", i+1); + + iface = (GSEProxy_Interface*) gsee_definitions[eng].create_fun(); + + iface->GSEProxy_Int_constr(this, i); + proxy_pool[i] = iface; +} + +GSEConnection::GSEConnection(CHAIN *nchain, int chainlen, nsock_pool nnsp, int ngss_pool_off){ + assert(nchain); + assert(nnsp); + proxy_pool = (class GSEProxy_Interface **)malloc(sizeof(class GSEProxy_Interface*)*chainlen); + proxy_pool_len = chainlen; + chain = nchain; + proxy_current = 0; + nsp = nnsp; + nsi = nsi_new(nnsp, this); + gss_pool_off = ngss_pool_off; + build_hop(0); + nsock_timer_create(nsp, gse_generic_handler, 0, proxy_pool[0]); +} + +GSEConnection::~GSEConnection(){ + for(int i=0;istate); +} + +bool GSEConnection::chain_built(){ + if(proxy_current >= proxy_pool_len-1) + return(true); + return(false); +} + +void GSEConnection::query_nexthop(){ + assert(proxy_current+1 < proxy_pool_len); + querynewport( chain[proxy_current+1].px_host, + chain[proxy_current+1].px_port, + IPPROTO_TCP); +}; + +void GSEConnection::jump_nexthop(){ + build_hop(++proxy_current); // :) +}; + + +void GSEConnection::querynewport(HOST *host, u16 port, u8 proto){ + proxy_pool[proxy_current]->querynewport(host, port, proto); +} + +struct gse_gps_tmp GSEConnection::getportstatus(){ + return(proxy_pool[proxy_current]->getportstatus()); +} + +CHAIN *GSEConnection::get_curr_chain(){ + return(proxy_pool[proxy_current]->chain); +} + + +/*************************************************************************/ +bool GSEScanner::get_new_target(HOST **target, u16 *port, u8 *proto){ + assert(target); + assert(port); + assert(proto); + + if(targetlist_pos == targetlist_len) + return(false); + + if(portarr_pos==portarr_len){ // move to next target + targetlist_pos++; + portarr_pos = 0; + if(targetlist_pos == targetlist_len) + return(false); + } + *target = targetlist[targetlist_pos]; + *port = portarr[portarr_pos++]; + *proto = protocol; + return(true); +} + +// how many ports left to scan? +int GSEScanner::get_portsleft(){ + int all = targetlist_len * portarr_len; + + // subtract already scanned hosts + all -= targetlist_pos * portarr_len; + + // subtract already scanned ports for current target + all -= portarr_pos; + return(all); +} + +int GSEScanner::get_active_conn(){ + int ct =0; + for(int i=0; i < connpool_len; i++) + if(connpool[i]) + ct++; + return(ct); +} + +void GSEScanner::GSEScanner_creat(int conn_max, + CHAIN *nchain, int nchain_len, + HOST **ntargetlist, int ntargetlist_len, + u16 *nportarr, int nportarrlen, u8 nprotocol) +{ + portarr = nportarr; + portarr_len = nportarrlen; + portarr_pos = 0; + assert(portarr && portarr_len>0); + + protocol = nprotocol; + assert(protocol==IPPROTO_TCP || protocol==IPPROTO_UDP); + + targetlist = ntargetlist; + targetlist_len = ntargetlist_len; + targetlist_pos = 0; + assert(targetlist && targetlist_len>0); + + chain = nchain; + chain_len = nchain_len; + assert(chain && chain_len>0); + + connpool_len = MIN(portarr_len*targetlist_len, conn_max); // we don't need more connections than ports to scan + connpool = (GSEConnection **)safe_zalloc(sizeof(GSEConnection*) * connpool_len); + assert(connpool_len > 0); + + nsp = nsp_new(this); + assert(nsp); + + conn_counter = 0; + debug = 0; + // create connections + for(int conn_off=0; conn_off < connpool_len; conn_off++){ + conn_counter++; + connpool[conn_off] = new GSEConnection(chain, chain_len, nsp, conn_off); + } +} + + + +GSEScanner::~GSEScanner(){ + for(int conn_off=0; conn_off < connpool_len; conn_off++) + if(connpool[conn_off]) + delete connpool[conn_off]; + if(connpool) + free(connpool); + nsp_delete(nsp); +} + +void GSEScanner::conn_remove(int conn_off){ + assert(connpool[conn_off]); + + if(debug) + error("WARNING: connection #%i: removing", conn_off); + + delete connpool[conn_off]; + connpool[conn_off] = NULL; + + // and create new if there is need: + // count currently active connections + int ct = get_active_conn(); + // if there is need, create new + if(ct < get_portsleft()){ // more ports than active slots + conn_counter++; + connpool[conn_off] = new GSEConnection(chain, chain_len, nsp, conn_off); + } +} + +void GSEScanner::conn_give_input(int conn_off){ + GSEConnection *conn = connpool[conn_off]; + assert(conn); + + // Are we in the last hop? + if(conn->chain_built()){ // last hop -> query for target! + // some ports left to scan + HOST *target; + u16 port; + u8 proto; + if(get_new_target(&target, &port, &proto)){ + if(debug) + error("WARNING: connection #%i: succeded, scanning host %s:%i/%s", + conn->gss_pool_off, + HOST_tostr(target) , port, proto==IPPROTO_TCP?"tcp":"udp"); + conn->querynewport(target, port, proto); + }else{ + if(debug) + error("WARNING: connection #%i: succeded, no more ports to scan. removing", + conn->gss_pool_off); + conn_remove(conn_off); + } + }else{ // middle hop + //error("WARNING: Connection #%i: connecting to next hop", i); + conn->query_nexthop(); + } +} + +void GSEScanner::conn_read_output(int conn_off){ + GSEConnection *conn = connpool[conn_off]; + assert(conn); + + struct gse_gps_tmp ggt; + + // read the status. + ggt = conn->getportstatus(); + + // is event from the last hop? + if(conn->chain_built()){ + int ps; + switch(ggt.reason){ + case GSER_CLOSED: + ps = PORT_CLOSED; + break; + case GSER_CLOSED_FILTERED: + ps = PORT_CLOSEDFILTERED; + break; + case GSER_GW_FILTERED: + case GSER_FILTERED: + ps = PORT_FILTERED; + break; + case GSER_OPENED: + ps = PORT_OPEN; + break; + case GSER_GW_ERROR: + case GSER_NONE: + ps = PORT_UNKNOWN; + break; + default: + assert(0); + } + if(ggt.reason == GSER_GW_ERROR) + fatal("ERROR: #%i: (hop %s-> target %s) is %s:%s", + conn->gss_pool_off, CHAIN_tostr(conn->get_curr_chain()), HOST_tostr(ggt.host), + gse_reason_tostr(ggt.reason), ggt.reason_txt); + if(debug) + error(" %s:%i/%s is %s:%s (#%i)", + HOST_tostr(ggt.host), ggt.port, ggt.protocol==IPPROTO_TCP?"tcp":"udp", + gse_reason_tostr(ggt.reason), ggt.reason_txt, conn->gss_pool_off); + handle_result(ggt.host, ggt.port, ggt.protocol, ps); + }else{ + if(ggt.reason != GSER_OPENED){ + error("WARNING: connection #%i: hop %s hasn't opened connection but is %s:%s", + conn->gss_pool_off, CHAIN_tostr(conn->get_curr_chain()), + gse_reason_tostr(ggt.reason), ggt.reason_txt); + fatal("ERROR: Cannot build a chain!"); + } + } + if(ggt.reason == GSER_GW_ERROR) + fatal("ERROR: Sorry, you have some problems with proxies"); +} + +void GSEScanner::conn_alive(int conn_off){ + GSEConnection *conn = connpool[conn_off]; + assert(conn); + + //error("WARNING: Connection #%i: alive", i); + // is event from the last hop? + if(conn->chain_built()){ // last hop -> just kill connection + handle_alive(conn); + }else{ // middle hop, go to the next one :) + conn->jump_nexthop(); + } +} + +bool GSEScanner::conn_exists(int conn_off){ + if(connpool[conn_off]) + return(true); + return(false); +} + +enum gse_states GSEScanner::conn_get_state(int conn_off){ + GSEConnection *conn = connpool[conn_off]; + assert(conn); + + return(conn->get_state()); +} + +nsock_pool GSEScanner::get_nsp(){ + return(nsp); +} + + + +/*************************************************************************/ +Pcre::Pcre(char *re, int re_opts){ + const char *pcre_errptr = NULL; + int pcre_erroffset = 0; + /* Compile regexp */ + regex_compiled = pcre_compile(re, re_opts, &pcre_errptr, + &pcre_erroffset, NULL); + if (regex_compiled == NULL) + fatal("Illegal regexp '%s' offset %d: %s\n", re, pcre_erroffset, pcre_errptr); + regex_extra = pcre_study(regex_compiled, 0, &pcre_errptr); + if (pcre_errptr != NULL) + fatal("Failed to pcre_study regexp '%s': %s\n", re, pcre_errptr); + + offset = 0; + rc = 0; + resu_pos = 0; +} + +int Pcre::match(char *text, int text_len){ + flush(); + rc = pcre_exec(regex_compiled, regex_extra, text, text_len, offset, 0, ovector, sizeof(ovector) / sizeof(*ovector)); + if (rc < 0) { +#ifdef PCRE_ERROR_MATCHLIMIT // earlier PCRE versions lack this + if (rc == PCRE_ERROR_MATCHLIMIT) + error("WARNING: Hit PCRE_ERROR_MATCHLIMIT when probing regexp '%s'", text); + else +#endif // PCRE_ERROR_MATCHLIMIT + if (rc != PCRE_ERROR_NOMATCH) + fatal("Unexpected PCRE error (%d) with the regex '%s'", rc, text); + rc = -1; + } +#if 0 + /* print results */ + //printf("got %i\n",rc); + for(int i=0; i0) + printf("%i->%s %x %i\n", i, txt,text,rc); + pcre_free_substring(txt); + } +#endif + return(rc); + +} + +void Pcre::move(){ + offset = ovector[1]; +} + +// must be done if move() is executed +void Pcre::init(){ + offset = 0; + flush(); +} + +char *Pcre::get_dup(char *text, int number){ + const char *txt; + pcre_get_substring(text, ovector, rc, number, &txt); + char *t = strdup(txt); + pcre_free_substring(txt); + return(t); +} + +char *Pcre::get(char *text, int number){ + const char *ctxt; + pcre_get_substring(text, ovector, rc, number, &ctxt); + char *txt = (char*)ctxt; + resu[resu_pos++] = txt; + if(resu_pos == sizeof(resu)/sizeof(char*)) + error("WARNING: too many elements extracted from regexp"); + return(txt); +} + +void Pcre::flush(){ + for(int i=0; imagic = HOSTMAGIC; + h->counter = 1; + h->hostname = strdup(hname); + h->ths = NULL; + return(h); +} +HOST *HOST_new(char *hostname, class Target *nths){ + HOST *host = HOST_new(hostname); + host->ths = nths; + return(host); +} + +class Target *HOST_get_ths(HOST *host){ + assert(host); + return(host->ths); +} + +HOST *HOST_copy(HOST *host){ + assert(host && host->magic==HOSTMAGIC); + host->counter++; + return(host); +} + +void HOST_delete(HOST *host){ + assert(host && host->magic==HOSTMAGIC); + host->counter--; + if(host->counter <= 0){ + if(host->ip) + free(host->ip); + if(host->hostname) + free(host->hostname); + if(host->sa) + free(host->sa); + free(host); + } +} + +struct sockaddr *HOST_getsa(HOST *host){ + assert(host && host->magic==HOSTMAGIC); + + if(host->sa) + return(host->sa); + + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + + if(!host->ip) + HOST_resolve(host); + if(host->af == AF_INET){ + sin = (struct sockaddr_in*)safe_malloc(sizeof(struct sockaddr_in)); + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr, host->ip, 4); + host->sa = (struct sockaddr*)sin; + } + if(host->af == AF_INET6){ + sin6 = (struct sockaddr_in6*)safe_malloc(sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, host->ip, 16); + host->sa = (struct sockaddr*)sin6; + } + return(host->sa); +}; + +size_t HOST_getsasize(HOST *host){ + assert(host && host->magic==HOSTMAGIC); + + if(!host->ip) + HOST_resolve(host); + if(host->af==AF_INET) + return(sizeof(struct sockaddr_in)); + if(host->af==AF_INET6) + return(sizeof(struct sockaddr_in6)); + return(0); +} + +/* Try to resolve hostname locally, no dns queries. */ +void HOST_resolve_simple(HOST *host){ + assert(host && host->magic==HOSTMAGIC); + assert(host->hostname); + + char tmp[16]; + if(host->ip) + return; + if(inet_pton(AF_INET, host->hostname, &tmp)>0){ + host->af = AF_INET; + host->ip = (u8*) malloc(4); + memcpy(host->ip, &tmp, 4); + return; + } + if(inet_pton(AF_INET6, host->hostname, &tmp)>0){ + host->af = AF_INET6; + host->ip = (u8*) malloc(16); + memcpy(host->ip, &tmp, 16); + return; + } + // TODO: check /etc/hosts etc. +} + +void HOST_resolve(HOST *host){ + HOST_resolve(host,true); +} +void HOST_resolve(HOST *host, bool printwarning){ + assert(host && host->magic==HOSTMAGIC); + assert(host->hostname); + + if(host->ip) + return; + HOST_resolve_simple(host); + if(host->ip) + return; + + if(printwarning==true){ + error("WARNING: host '%s' is resolved on LOCAL SIDE, not on remote.", host->hostname); + sleep(1); + } + if(o.noresolve) + fatal("Error while resolving '%s'. Host resolving is forbidden by command line.", host->hostname); + + + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + size_t a; + if(resolve(host->hostname, (struct sockaddr_storage *)&sin, &a, AF_INET)){ + assert(a==sizeof(sin)); + host->ip = (u8*) safe_malloc(4); + memcpy(host->ip, &sin.sin_addr, 4); + host->af = AF_INET; + }else if(resolve(host->hostname, (struct sockaddr_storage *)&sin6, &a, AF_INET6)){ + assert(a==sizeof(sin6)); + host->ip = (u8*) safe_malloc(16); + memcpy(host->ip, &sin6.sin6_addr, 16); + host->af = AF_INET6; + }else + fatal("Resolving '%s' failed", host->hostname); +} + +char *HOST_tostr(HOST *host){ + if(!host) + return(""); + + assert(host->magic==HOSTMAGIC); + + static char sip[64]; + + if(host->ip){ + assert(host->af==AF_INET || host->af==AF_INET6); + if(inet_ntop(host->af, host->ip, sip, sizeof(sip))==NULL) + assert(0); + return(sip); + } + return(host->hostname); +} + +bool HOST_isipv6(HOST *host){ + assert(host && host->magic==HOSTMAGIC); + + if(!host->ip) + HOST_resolve(host); + if(host->af == AF_INET6) + return(true); + return(false); +} + + + +void nsockbuf_handler(nsock_pool nsp, nsock_event nse, void *ud); + + +struct nsockbuf_priv{ + int bytes_requested; + int lines_requested; + + char *buf; + int buf_len; + int buf_allocated; + char *buf_free; //to be freed; + + // user's data :) + nsock_pool nsp; + nsock_iod nsi; + void *userdata; + nsock_ev_handler callback; +}; + +/* +struct map_compare{ + bool operator()(nsock_iod nsi1, nsock_iod nsi2) const{ + return(nsi1 < nsi2); + } +}; +*/ + +map nsockbuf_map; +map nsockbuf_ev_map; + +void nsockbuf_tidyup(){ + map::iterator mi; + for(mi=nsockbuf_map.begin(); mi != nsockbuf_map.end(); mi++){ + struct nsockbuf_priv *nsbp = mi->second; + assert(nsbp); + if(nsbp->buf_free){ + free(nsbp->buf_free); + nsbp->buf_free = NULL; + } + if(nsbp->buf && nsbp->buf_len==0){ + free(nsbp->buf); + nsbp->buf = NULL; + } + if(!nsbp->buf && nsbp->bytes_requested==0 && nsbp->lines_requested==0){ + nsockbuf_map.erase(nsbp->nsi); + free(nsbp); + } + } +} +void nsockbuf_flush(nsock_iod nsi){ + assert(nsi); + struct nsockbuf_priv *nsbp = nsockbuf_map[nsi]; + if(!nsbp) + return; + if(nsbp->buf_len) + nsbp->buf_len = 0; + if(nsbp->buf_free){ + free(nsbp->buf_free); + nsbp->buf_free = NULL; + } + if(nsbp->buf){ + free(nsbp->buf); + nsbp->buf = NULL; + } +} + +void nsockbuf_free(nsock_iod nsi){ + assert(nsi); + struct nsockbuf_priv *nsbp = nsockbuf_map[nsi]; + if(!nsbp) + return; + + if(nsbp->bytes_requested!=0 || nsbp->lines_requested!=0) + if(o.debugging>2) + 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); + + // nsockbuf_tidyup(); +} + + +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 = (struct nsockbuf_priv *)safe_zalloc(sizeof(struct nsockbuf_priv)); + nsbp->nsp = nsp; + nsbp->nsi = nsi; + nsockbuf_map[nsi] = nsbp; + } + 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; + + nsbp->callback = handler; + nsbp->userdata = userdata; + + if( nsbp->buf && memchr(nsbp->buf, '\n', nsbp->buf_len) ){ // already have data! + nse = nsock_timer_create(nsbp->nsp, nsockbuf_handler, 0, nsbp); + nsockbuf_ev_map[nse] = nsbp; + }else + nse = nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, timeout_msecs, nsbp); + return(nse); +} + +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 = (struct nsockbuf_priv *)safe_zalloc(sizeof(struct nsockbuf_priv)); + nsbp->nsp = nsp; + nsbp->nsi = nsi; + nsockbuf_map[nsi] = nsbp; + } + 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; + + nsbp->callback = handler; + nsbp->userdata = userdata; + + if( nsbp->buf && nsbp->buf_len>=nsbp->bytes_requested ){ // already have data! + nse = nsock_timer_create(nsbp->nsp, nsockbuf_handler, 0, nsbp); + nsockbuf_ev_map[nse] = nsbp; + }else + nse = nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, timeout_msecs, nsbp); + return(nse); +} + + +char *nsockbuf_getlines(struct nsockbuf_priv *nsbp, int* rec_len){ + assert(nsbp); + assert(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'; + memmove(nsbp->buf, &nsbp->buf[out_len], nsbp->buf_len - out_len); + nsbp->buf_len -= out_len; + if(nsbp->buf_len == 0){ + free(nsbp->buf); + nsbp->buf = NULL; + nsbp->buf_len = 0; + nsbp->buf_allocated = 0; + } + + nsbp->buf_free = out; + *rec_len = out_len; + nsbp->lines_requested = 0; + + nsbp->userdata = NULL; + nsbp->callback = NULL; + return(out); + } + error("nsockbuf_getlines called but no data availble"); + return(NULL); +} + +char *nsockbuf_getbytes(struct nsockbuf_priv *nsbp, int* rec_len){ + assert(nsbp); + assert(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); + memcpy(out, nsbp->buf, out_len); + memmove(nsbp->buf, &nsbp->buf[out_len], nsbp->buf_len - out_len); + nsbp->buf_len -= out_len; + if(nsbp->buf_len == 0){ + free(nsbp->buf); + nsbp->buf = NULL; + nsbp->buf_len = 0; + nsbp->buf_allocated = 0; + } + + nsbp->buf_free = out; + *rec_len = out_len; + nsbp->bytes_requested = 0; + + nsbp->userdata = NULL; + nsbp->callback = NULL; + return(out); + } + error("nsockbuf_getbytes called but no data availble"); + return(NULL); +} + + + + + +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(!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); + } + // ugly hack :) + struct msevent *mse = (struct msevent*)nse; + mse->type = NSE_TYPE_READ; + nsbp->callback(nsp, nse, nsbp->userdata); + }else + nsbp->callback(nsp, nse, nsbp->userdata); +} + +char *nsockbuf_readbuf(nsock_event nse, int *nbytes){ + nsock_iod nsi = nse_iod(nse); + + struct nsockbuf_priv *nsbp; + if(nsi){ + nsbp = nsockbuf_map[nsi]; + }else{ + nsbp = nsockbuf_ev_map[nse_id(nse)]; + nsockbuf_ev_map.erase(nse_id(nse)); + } + assert(nsbp); + + + char *p = NULL; + if(nsbp->bytes_requested){ + p = nsockbuf_getbytes(nsbp, nbytes); + }else{ + if(nsbp->lines_requested) + p = nsockbuf_getlines(nsbp, nbytes); + } + if(p){ + nsbp->callback = NULL; + nsbp->userdata = NULL; + } + return(p); +} + +bool nsockbuf_empty(nsock_iod nsi){ + struct nsockbuf_priv *nsbp = NULL; + if(nsi) + nsbp = nsockbuf_map[nsi]; + if(!nsbp) + return(true); + if(nsbp->buf_len) + return(false); + return(true); +} + + +long nsockbuf_geteventtime(nsock_event vnse){ + msevent *nse = (msevent *)vnse; + long d = TIMEVAL_MSEC_SUBTRACT(*nsock_gettimeofday(), nse->time_created); + return(d); + +} + + + + + +/* Remember to update also 'enum gse_engines' */ +struct gse_eng_definitions gsee_definitions[] = { + {GSEENG_UNINITIALISED, GSEF_NONE, NULL, {NULL} }, + {GSEENG_NSOCK, GSEF_UDP|GSEF_IPV6|GSEF_FIRST, GSEProxy_NSock_new, {"nsock",NULL} }, + {GSEENG_HTTPCONNECT, GSEF_DNS|GSEF_IPV6, GSEProxy_HttpConnect_new,{"httpconnect","httpc", "hc",NULL} }, + {GSEENG_HTTPGET, GSEF_DNS|GSEF_IPV6|GSEF_LAST, GSEProxy_HttpGet_new, {"httpget","httpg","hg",NULL} }, + {GSEENG_SOCKS4A, GSEF_DNS, GSEProxy_Socks4a_new, {"socks4a","socks4","s4","s4a",NULL} }, + {GSEENG_FTPBOUNCE, GSEF_LAST, GSEProxy_FtpBounce_new, {"ftpbounce","ftpb","ftp","fb",NULL} }, + {GSEENG_IMAP, GSEF_LAST, GSEProxy_Imap_new, {"imap","im",NULL} }, +}; + + +// number of elements in gsee_definitions +int gsee_definitions_len = sizeof(gsee_definitions)/sizeof(struct gse_eng_definitions); + +void GSEProxy_Interface::handler(enum gse_events ev, nsock_event nse){ + enum nse_status status = nse_status(nse); + enum nse_type type = nse_type(nse); + ev = ev; // I hate warnings like 'unused parameter' +fatal("ERROR: This handler never should be runned directly (%s:%s)!", + nse_type2str(type), nse_status2str(status)); +} + +void GSEProxy_Interface::querynewport(HOST *host, u16 port, u8 proto){ + assert(state == GSS_WAITING_FOR_INPUT); + + assert(host); + assert(proto==IPPROTO_TCP || proto==IPPROTO_UDP); + + + if( proto == IPPROTO_UDP && + gsee_definitions[chain->engine].flags & GSEF_UDP) + fatal("ERROR: %s does not support udp!", CHAIN_tostr(chain)); + if( gsee_definitions[chain->engine].flags & GSEF_DNS == 0 && + gsee_definitions[chain->engine].flags & GSEF_IPV6 && + HOST_isipv6(host)) + fatal("ERROR: %s does not support ipv6!", CHAIN_tostr(chain)); + + target_proto = proto; + target_port = port; + if(target_host) + HOST_delete(target_host); + target_host = HOST_copy(host); + + // clean up! + reason = GSER_NONE; + reason_txt[0] = '\0'; + + // reset timeouts to the target to current proxy timeouts + if(target_host && target_host->ths && target_host->ths->to.srtt == -1){ + target_host->ths->to = chain->to; + } + + handler(GSEE_NOEVENT, NULL); +} + +struct gse_gps_tmp GSEProxy_Interface::getportstatus(){ + struct gse_gps_tmp ggt; + memset(&ggt, 0, sizeof(ggt)); + + ggt.host = target_host; + ggt.port = target_port; + ggt.protocol = target_proto; + ggt.reason = reason; + strncpy(ggt.reason_txt, reason_txt, sizeof(ggt.reason_txt)); + + handler(GSEE_NOEVENT, NULL); + return(ggt); +} + +void GSEProxy_Interface::GSEProxy_Int_constr(GSEConnection *ngsc, int nchainpos){ + assert(ngsc); + + gsc = ngsc; + reason = GSER_NONE; + reason_txt[0] = '\0'; + target_host = NULL; + target_port = 0; + target_proto = 0; + + chain = &gsc->chain[nchainpos]; + assert(chain); + + state = GSS_INTERNAL_STATE_0; + handler(GSEE_NOEVENT, NULL); +} + +void GSEProxy_Interface::set_reason_txt(const char *fmt, ...){ + va_list ap; + char buf[128]; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf),fmt,ap); + va_end(ap); + + snprintf(reason_txt, sizeof(reason_txt),"(hop %s:%i) %s", HOST_tostr(chain->px_host), chain->px_port, buf); + + return; +} + +GSEProxy_Interface::~GSEProxy_Interface(){ + if(target_host) + HOST_delete(target_host); +} + +char *GSEProxy_Interface::tostr(){ + static char a[64]; + snprintf(a,sizeof(a),"%s:%i/%s", + HOST_tostr(target_host), target_port, + target_proto==IPPROTO_TCP?"tcp":"udp"); + return(a); +} + +long GSEProxy_Interface::get_timeout(){ + if(target_host && target_host->ths){ // real 'Target*' + last_gto = GSE_TARGET; + return(target_host->ths->to.timeout / 1000); // in miliseconds, not micro + } + // return proxies times :) + assert(chain); + last_gto = GSE_PROXY; + return(chain->to.timeout / 1000); +} + +long GSEProxy_Interface::get_timeout(enum gse_timeout_owner gto){ + if(gto == GSE_TARGET){ + if(target_host && target_host->ths){ // real 'Target*' + last_gto = GSE_TARGET; + return(target_host->ths->to.timeout / 1000); // in miliseconds, not micro + }else + error("WARNING: get_timeout(from target) but no target structure present!"); + } + // return proxies times :) + assert(chain); + last_gto = GSE_PROXY; + return(chain->to.timeout / 1000); +} + +struct timeout_info *GSEProxy_Interface::get_timeout_info(){ + if(last_gto==GSE_TARGET && target_host && target_host->ths) + return(&target_host->ths->to); + assert(chain); + return(&chain->to); +} + +/**************************************************************************** + * SCANNING ENGINE nsock * + * http://ai.pjwstk.edu.pl/~majek/private/nmap/state-nsock.png * + ****************************************************************************/ +class GSEProxy_NSock: public GSEProxy_Interface{ +public: + void handler(enum gse_events eve,nsock_event nse){ + + switch(state){ + case GSS_INTERNAL_STATE_0: + assert(eve == GSEE_NOEVENT); + state = GSS_WAITING_FOR_INPUT; + break; + case GSS_INTERNAL_STATE_1: + switch(eve){ + case GSEE_NOEVENT: + assert(target_host); + nsock_connect_tcp( gsc->nsp, gsc->nsi, + gse_generic_handler, + get_timeout(), + this, /* user data ! */ + HOST_getsa(target_host), + HOST_getsasize(target_host), + target_port); + reason = GSER_NONE; + break; + case GSEE_TIMEOUT: + set_reason_txt("timeouted!"); + reason = GSER_FILTERED; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_EOF: + reason = GSER_CLOSED; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_CONNECT: + reason = GSER_OPENED; + state = GSS_WAITING_FOR_OUTPUT; + break; + default: + assert(0); + } + break; + case GSS_WAITING_FOR_INPUT: + assert(target_proto == IPPROTO_TCP); + + state = GSS_INTERNAL_STATE_1; + handler(GSEE_NOEVENT, nse); + break; + case GSS_WAITING_FOR_OUTPUT: + if(reason == GSER_OPENED) + state = GSS_ALIVE; + else + state = GSS_DEAD; + break; + case GSS_DEAD: + case GSS_ALIVE: + default: + assert(0); + break; + } + } + +}; + +void *GSEProxy_NSock_new(){ + return(new GSEProxy_NSock); +} + + + +/**************************************************************************** + * SCANNING ENGINE http CONNECT/GET * + * http://ai.pjwstk.edu.pl/~majek/private/nmap/state-httpconnect.png * + ****************************************************************************/ + +enum http_type{ + HTTPCONNECT, + HTTPGET, +}; + +class GSEProxy_HttpConnect: public GSEProxy_Interface{ + int http_code; + int http_len; + bool read_eof; + Pcre *re_http_code; + Pcre *re_http_len; + Pcre *re_http_none; + + int http_type; + char *http_format_str; +public: + void handler(enum gse_events eve,nsock_event nse); + + +public: + GSEProxy_HttpConnect(int nhttp_type); + ~GSEProxy_HttpConnect(); +}; + +GSEProxy_HttpConnect::GSEProxy_HttpConnect(int nhttp_type){ + re_http_code = new Pcre("^HTTP/1\\.[01] ([0-9]{3})", 0); + re_http_len = new Pcre("^Content-Length:[ \t]+([0-9]+)", PCRE_CASELESS); + re_http_none = new Pcre("^[\r]?$",0); + http_type = nhttp_type; + http_format_str = ""; + if(http_type == HTTPCONNECT){ + http_format_str = + "CONNECT %s:%i HTTP/1.0\n" // we don't support chunking yet + "Proxy-Connection: Keep-Alive\n" + "Host: %s:%i\n" + "\n"; + }else{ + http_format_str = + "GET http://%s:%i/ HTTP/1.0\n" + "Proxy-Connection: Keep-Alive\n" + "Host: %s:%i\n" + "\n"; + } +} +GSEProxy_HttpConnect::~GSEProxy_HttpConnect(){ + if(re_http_code) + delete re_http_code; + if(re_http_len) + delete re_http_len; + if(re_http_none) + delete re_http_none; +} + + +void GSEProxy_HttpConnect::handler(enum gse_events eve,nsock_event nse){ + char *rec; + int rec_len=0; + + switch(state){ + case GSS_INTERNAL_STATE_0: + assert(eve == GSEE_NOEVENT); + state = GSS_WAITING_FOR_INPUT; + break; + case GSS_WAITING_FOR_INPUT: + assert(target_proto == IPPROTO_TCP); + + state = GSS_INTERNAL_STATE_1; + handler(GSEE_NOEVENT, nse); + break; + case GSS_INTERNAL_STATE_1: + switch(eve){ + case GSEE_NOEVENT: + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(), + this, + http_format_str, + HOST_tostr(target_host), target_port, + HOST_tostr(target_host), target_port); + + http_code = 0; + http_len = 0; + read_eof = false; + + nsockbuf_readline(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(), + this); + break; + case GSEE_EOF: + set_reason_txt("connection closed while reading http header"); + if(http_type == HTTPGET && + nsockbuf_empty(gsc->nsi)==true && + http_code==0 && http_len==0) // for example ssh daemon + reason = GSER_OPENED; + else + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_TIMEOUT: + set_reason_txt("connection timeouted while reading http header"); + reason = GSER_FILTERED;//GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); // get one line + read_eof = nse_eof(nse); + + if(re_http_none->match(rec, rec_len) >= 0){ // end of header. -> null line + state = GSS_INTERNAL_STATE_2; + handler(GSEE_NOEVENT, nse); + return; + } + + if(re_http_code->match(rec, rec_len) >= 0){ // HTTP/1.0 code + int nhttp_code = atoi(re_http_code->get(rec, 1)); + if(http_code) // already set + error("WARNING: Duplicate 'HTTP' header, http code already set to %i, new %i.", http_code, nhttp_code); + http_code = nhttp_code; + if(o.debugging > 1) + error("HTTP: http-code %i\n",http_code); + } + if(re_http_len->match(rec, rec_len) >= 0){ // Content-Length: + int nhttp_len = atoi(re_http_len->get(rec, 1)); + + if(http_len) // already set + error("WARNING: Duplicate 'Content-Length' header, http len already set to %i, new %i.", http_len, nhttp_len); + http_len = nhttp_len; + if(o.debugging > 1) + error("HTTP: http-len %i\n", http_len); + } + // header not ended, continue :) + nsockbuf_readline(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(), + this); + break; + default: + assert(0); + } + + break; + case GSS_INTERNAL_STATE_2: //we received header + assert(eve == GSEE_NOEVENT); + if(http_code == 0){ // not set + if(http_type == HTTPGET){ + reason = GSER_OPENED; + }else{ + set_reason_txt("http code not set, is this really http proxy?"); + reason = GSER_GW_ERROR; + } + }else if(http_code == 400){ + set_reason_txt("400: bad request on gateway %s:%i!"); + reason = GSER_GW_FILTERED; + }else if(http_code == 403){ + reason = GSER_GW_FILTERED; + }else if(http_code == 503){ + reason = GSER_CLOSED; + }else if(http_code == 200){ + reason = GSER_OPENED; + }else if(http_code == 404){ + set_reason_txt("http: not found. Is the hostname valid?"); + reason = GSER_GW_ERROR; + }else{ + set_reason_txt("can't understand HTTP code %i", http_code); + reason = GSER_GW_ERROR; + } + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSS_WAITING_FOR_OUTPUT: + if(reason==GSER_OPENED && http_type==HTTPCONNECT){ + state = GSS_ALIVE; + }else if(http_code && !read_eof){ // try to reuse this connection + state = GSS_INTERNAL_STATE_3; + handler(GSEE_NOEVENT, nse); + return; + }else // http code not set or connection closed + state = GSS_DEAD; + break; + case GSS_INTERNAL_STATE_3: // try to resue connection, first receive body + switch(eve){ + case GSEE_NOEVENT: + if(http_len){ + nsockbuf_readbytes(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(), + this, http_len); + }else{ + set_reason_txt("no 'Content-Length' in http header"); + state = GSS_DEAD; + return; + } + break; + case GSEE_READ: // okay, body received. + rec = nsockbuf_readbuf(nse, &rec_len); + read_eof = nse_eof(nse); + // Now the ethereal question. Is the connection closed? + if(read_eof){ + state = GSS_DEAD; + return; + } + // we don't know still if connection is closed. try to read something. + state = GSS_INTERNAL_STATE_4; + handler(GSEE_NOEVENT, nse); + return; + case GSEE_TIMEOUT: + state = GSS_DEAD; + break; + case GSEE_EOF: + state = GSS_DEAD; + break; + default: + assert(0); + } + break; + case GSS_INTERNAL_STATE_4: //read something from connection. wait for closing :) + switch(eve){ + case GSEE_NOEVENT: + nsockbuf_flush(gsc->nsi); + if(o.debugging > 1) + error("HTTP: waiting for FIN\n"); + nsock_read(gsc->nsp, gsc->nsi, + gse_generic_handler, MIN(get_timeout()/4,1000), // much less time + this); + break; + case GSEE_READ: + state = GSS_DEAD; + break; + case GSEE_TIMEOUT: // good! not closed :) + state = GSS_WAITING_FOR_INPUT; + break; + case GSEE_EOF: + state = GSS_DEAD; + break; + default: + assert(0); + } + break; + case GSS_DEAD: + case GSS_ALIVE: + default: + assert(0); + break; + } +} + +void *GSEProxy_HttpConnect_new(){ + return(new GSEProxy_HttpConnect(HTTPCONNECT)); +} + +void *GSEProxy_HttpGet_new(){ + return(new GSEProxy_HttpConnect(HTTPGET)); +} + + +/**************************************************************************** + * SCANNING ENGINE socks4a * + * http://ai.pjwstk.edu.pl/~majek/private/nmap/state-socks4a.png * + ****************************************************************************/ + +class GSEProxy_Socks4a: public GSEProxy_Interface{ + static bool printed_warnings; +public: + void handler(enum gse_events event,nsock_event nse); +}; + + +struct socks4_request{ + u8 version; + u8 command; + u16 port; + u8 addr[4]; + char username[256]; +}; + +struct socks4_answer{ + u8 version; + u8 command; + u16 port; + u8 addr[4]; +}; + + +bool GSEProxy_Socks4a::printed_warnings = false; + +void GSEProxy_Socks4a::handler(enum gse_events eve,nsock_event nse){ + char *rec; + int rec_len=0; + struct socks4_request s4r; + int s4rlen=0; + int beg; + struct socks4_answer *s4a; + + switch(state){ + case GSS_INTERNAL_STATE_0: + assert(eve == GSEE_NOEVENT); + state = GSS_WAITING_FOR_INPUT; + break; + case GSS_INTERNAL_STATE_1: + switch(eve){ + case GSEE_NOEVENT: + memset(&s4r, 0, sizeof(s4r)); + s4r.version = 0x04; + s4r.command = 0x01; // CONNECT + HOST_resolve_simple(target_host); + if(chain->user && chain->user[0]!='\0'){ + if(!printed_warnings){ + error("WARNING: Authorization for socks4a is based on ident. See RFC 1413."); + if(chain->pass && chain->pass[0]!='\0') + error("WARNING: I'm not exacly sure what to do with password for socks4a."); + printed_warnings = true; + } + snprintf(s4r.username, sizeof(s4r.username), "%s",chain->user); + } + beg = strlen(s4r.username)+1; // append after null + + if(target_host->af == AF_INET6) + fatal("Sorry. Socks4a doesn't support ipv6 addresses"); + if(target_host->af == 0){ // socks4a + int e = snprintf(&s4r.username[beg], sizeof(s4r.username)-beg, "%s", HOST_tostr(target_host)); + s4rlen = sizeof(s4r) - sizeof(s4r.username) + beg + e+1; // after second null + while(s4r.addr[3]==0) + s4r.addr[3] = get_random_u8(); + }else{ // pure socks4 + memcpy(s4r.addr, target_host->ip, 4); + s4rlen = sizeof(s4r) - sizeof(s4r.username) + beg; + } + s4r.port = htons(target_port); + nsock_write(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(), + this, (char*)&s4r, s4rlen); + + nsock_read(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(), + this); // one packet + break; + case GSEE_EOF: + set_reason_txt("connection closed"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_TIMEOUT: + set_reason_txt("connection timeouted"); + reason = GSER_FILTERED; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + state = GSS_INTERNAL_STATE_2; + handler(GSEE_NOEVENT, nse); + break; + default: + assert(0); + } + break; + case GSS_INTERNAL_STATE_2: + assert(eve == GSEE_NOEVENT); + assert(nse); + // data is ready for reading + + rec = nse_readbuf(nse, &rec_len); + if(rec_len != 8 || rec[0]!=0 || rec[1]<90 || rec[1]>93){ + set_reason_txt("can't understand socks server response"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + } + assert(rec_len == sizeof(struct socks4_answer)); + s4a = (struct socks4_answer *)rec; + + //printf("%s:%i\n", inet_ntop(AF_INET, s4a->addr, buf, sizeof(buf)),ntohs(s4a->port)); + if(s4a->command == 90){ + reason = GSER_OPENED; + state = GSS_WAITING_FOR_OUTPUT; + }else if(s4a->command == 91){ + reason = GSER_CLOSED_FILTERED; + state = GSS_WAITING_FOR_OUTPUT; + }else{ + set_reason_txt("socks server rejected your authorization (code=%i)",s4a->command); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + } + break; + case GSS_WAITING_FOR_INPUT: + assert(target_proto == IPPROTO_TCP); + + state = GSS_INTERNAL_STATE_1; + handler(GSEE_NOEVENT, nse); + break; + case GSS_WAITING_FOR_OUTPUT: + if(reason == GSER_OPENED) + state = GSS_ALIVE; + else + state = GSS_DEAD; + break; + case GSS_DEAD: + case GSS_ALIVE: + default: + assert(0); + break; + } +} + +void *GSEProxy_Socks4a_new(){ + return(new GSEProxy_Socks4a); +} + + + +/**************************************************************************** + * SCANNING ENGINE socks4a * + * http://ai.pjwstk.edu.pl/~majek/private/nmap/state-ftpbounce.png * + ****************************************************************************/ + +#define FTPUSER "anonymous" +#define FTPPASS "-wwwuser@" + + +class GSEProxy_FtpBounce: public GSEProxy_Interface{ + static bool allow_priviledged; + static bool printed_warning; + Pcre *re_answer; +public: + void handler(enum gse_events eve,nsock_event nse); + + GSEProxy_FtpBounce(); + ~GSEProxy_FtpBounce(); +}; + + +GSEProxy_FtpBounce::GSEProxy_FtpBounce(){ + re_answer = new Pcre("^([0-9]{3}) (.*)\r?$", 0); +} + + +GSEProxy_FtpBounce::~GSEProxy_FtpBounce(){ + if(re_answer) + delete re_answer; +} + +bool GSEProxy_FtpBounce::allow_priviledged = true; +bool GSEProxy_FtpBounce::printed_warning = false; + +void GSEProxy_FtpBounce::handler(enum gse_events eve,nsock_event nse){ + char *rec; + int rec_len=0; + int ftp_code=0; + u16 portno, p1,p2; + + switch(state){ + case GSS_INTERNAL_STATE_0: // get banner + switch(eve){ + case GSEE_NOEVENT: + // wait at least 15 seconds. (for banner) + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, + get_timeout(GSE_PROXY)+15000, this); // one line + break; + case GSEE_EOF: + case GSEE_TIMEOUT: + if(eve == GSEE_EOF) + set_reason_txt("connection closed"); + else + set_reason_txt("connection timeouted"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); + if(re_answer->match(rec, rec_len) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > 1) + error("FTPBOUNCE: 0 %i\n",ftp_code); + if(ftp_code<200 || ftp_code>299){ + set_reason_txt("error while reading banner: %i %s", ftp_code, re_answer->get(rec, 2)); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + return; + } + state = GSS_INTERNAL_STATE_1; + handler(GSEE_NOEVENT, nse); + return; + } + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_PROXY)+15000, this); // one line + break; + default: + assert(0); + } + break; + case GSS_INTERNAL_STATE_1: // send username + switch(eve){ + case GSEE_NOEVENT: + if(!chain->user || chain->user[0]=='\0' || + !chain->pass || chain->pass[0]=='\0') + { + if(o.verbose && printed_warning == false){ + error("WARNING: user/pass not specifid for ftp-bounce, using %s:%s", FTPUSER, FTPPASS); + printed_warning = true; + } + if(chain->user) free(chain->user); + chain->user = strdup(FTPUSER); + + if(chain->pass) free(chain->pass); + chain->pass = strdup(FTPPASS); + } + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_PROXY),this, + "USER %s\r\n", chain->user); + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_PROXY), this); // one line + break; + case GSEE_EOF: + case GSEE_TIMEOUT: + if(eve == GSEE_EOF) + set_reason_txt("connection closed"); + else + set_reason_txt("connection timeouted"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); + if(re_answer->match(rec, rec_len) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > 1) + error("FTPBOUNCE: 1 %i\n",ftp_code); + if(ftp_code>=500 && ftp_code<=599){ + set_reason_txt("bad username: %i %s", ftp_code, re_answer->get(rec, 2)); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + return; + }// good username :) + state = GSS_INTERNAL_STATE_2; + handler(GSEE_NOEVENT, nse); + return; + } + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_PROXY), this); // one line + break; + default: + assert(0); + } + break; + case GSS_INTERNAL_STATE_2: // send password + switch(eve){ + case GSEE_NOEVENT: + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_PROXY), this, + "PASS %s\r\n", chain->pass); + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_PROXY), this); // one line + break; + case GSEE_EOF: + case GSEE_TIMEOUT: + if(eve == GSEE_EOF) + set_reason_txt("connection closed"); + else + set_reason_txt("connection timeouted"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); + if(re_answer->match(rec, rec_len) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > 1) + error("FTPBOUNCE: 2 %i\n",ftp_code); + if(ftp_code>=500 && ftp_code<=599){ + set_reason_txt("bad password: %i %s", ftp_code, re_answer->get(rec, 2)); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + return; + }// good password, we're ready for scanning + state = GSS_WAITING_FOR_INPUT; + return; + } + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_PROXY), this); // one line + break; + default: + assert(0); + } + break; + case GSS_WAITING_FOR_INPUT: + assert(target_proto == IPPROTO_TCP); + HOST_resolve_simple(target_host); + assert(target_host && target_host->ip); + + if(allow_priviledged==false && target_port < 1024){ + set_reason_txt("priviledged ports skipped"); + reason = GSER_GW_FILTERED; + state = GSS_WAITING_FOR_OUTPUT; + break; + } + + state = GSS_INTERNAL_STATE_3; + handler(GSEE_NOEVENT, nse); + break; + case GSS_INTERNAL_STATE_3: // send PORT command + switch(eve){ + case GSEE_NOEVENT: + portno = htons(target_port); + p1 = ((u8 *) &portno)[0]; + p2 = ((u8 *) &portno)[1]; + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(), this, + "PORT %d,%d,%d,%d,%i,%i\r\n", + UC(target_host->ip[0]), UC(target_host->ip[1]), + UC(target_host->ip[2]), UC(target_host->ip[3]), + p1, p2); + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(), this); // one line + break; + case GSEE_EOF: + case GSEE_TIMEOUT: + if(eve == GSEE_EOF) + set_reason_txt("connection closed"); + else + set_reason_txt("connection timeouted"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); + if(re_answer->match(rec, rec_len) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > 1) + error("FTPBOUNCE: 3 %i\n",ftp_code); + if(ftp_code>=500 && ftp_code<=599){ + if(target_port>0 && target_port<1024 && allow_priviledged==true){ + error("WARNING: Ftp bounce server doesn't allow privileged ports, marking them as FILTERED"); + allow_priviledged = false; + set_reason_txt("priviledged ports skipped"); + reason = GSER_GW_FILTERED; + state = GSS_WAITING_FOR_OUTPUT; + return; + } + if(target_port>0) + fatal("Your ftp bounce server sucks, it won't let us feed bogus ports!"); + set_reason_txt("%i %s", ftp_code, re_answer->get(rec, 2)); + reason = GSER_GW_FILTERED; + state = GSS_WAITING_FOR_OUTPUT; + return; + }// port command accepted :) + state = GSS_INTERNAL_STATE_4; + handler(GSEE_NOEVENT, nse); + return; + } + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(), this); // one line + break; + default: + assert(0); + } + break; + case GSS_INTERNAL_STATE_4: // send LIST command + switch(eve){ + case GSEE_NOEVENT: + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(), this, + "LIST\r\n"); + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout()+5000, this); // one line + break; + case GSEE_EOF: + set_reason_txt("connection closed"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_TIMEOUT: + set_reason_txt("connection timeouted"); + reason = GSER_FILTERED; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); + if(re_answer->match(rec, rec_len) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > 1) + error("FTPBOUNCE: 4 %i\n",ftp_code); + if(ftp_code == 150){ // wait for next message + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout()+5000, this); // one line + break; + } + if(ftp_code == 425 || ftp_code == 426){ + reason = GSER_CLOSED; + state = GSS_WAITING_FOR_OUTPUT; + break; + } + if(ftp_code == 226){ + reason = GSER_OPENED; + state = GSS_WAITING_FOR_OUTPUT; + break; + } + set_reason_txt("bad ftp response: %i %s", ftp_code, re_answer->get(rec, 2)); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + return; + } + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout()+5000, this); // one line + break; + default: + assert(0); + } + break; + case GSS_WAITING_FOR_OUTPUT: + if(reason == GSER_GW_ERROR || + reason == GSER_FILTERED) + state = GSS_DEAD; + else + state = GSS_WAITING_FOR_INPUT; + break; + case GSS_DEAD: + default: + assert(0); + break; + } +} + +void *GSEProxy_FtpBounce_new(){ + return(new GSEProxy_FtpBounce); +} + +/* +* OK [CAPABILITY IMAP4REV1 LOGIN-REFERRALS AUTH=CRAM-MD5 AUTH=PLAIN AUTH=LOGIN] [192.168.1.4] IMAP4rev1 2003.339 at Sun, 23 Jul 2006 04:24:29 +0200 (CEST) +1 LOGIN user pass +1 OK [CAPABILITY IMAP4REV1 IDLE NAMESPACE MAILBOX-REFERRALS BINARY UNSELECT SCAN SORT THREAD=REFERENCES THREAD=ORDEREDSUBJECT MULTIAPPEND] User xx authenticated +1 SELECT "{127.0.0.1:22}" +// timeout + +1 SELECT "{127.0.0.1:23}" +1 NO SELECT failed: Connection failed to 127.0.0.1,23: Connection refused + +1 SELECT "{127.0.0.1:143}" +1 NO SELECT failed: Certificate failure for 127.0.0.1: self signed certificate: /O=University of Washington IMAP daemon/OU=localhost/CN=localhost.localdomain/emailAddress=root@ +*/ + +/**************************************************************************** + * SCANNING ENGINE imap * + * http://ai.pjwstk.edu.pl/~majek/private/nmap/state-imap.png * + ****************************************************************************/ +/* see: http://lists.darklab.org/pipermail/darklab/2006-January/000185.html */ + +class GSEProxy_Imap: public GSEProxy_Interface{ + Pcre *re_answer; +public: + void handler(enum gse_events event,nsock_event nse); + + GSEProxy_Imap(); + ~GSEProxy_Imap(); +}; + + +void *GSEProxy_Imap_new(){ + return(new GSEProxy_Imap); +} + +GSEProxy_Imap::GSEProxy_Imap(){ + re_answer = new Pcre("^[0-9]+ ([^ ]+) (.*)\r?$", 0); +} + + +GSEProxy_Imap::~GSEProxy_Imap(){ + if(re_answer) + delete re_answer; +} + +void GSEProxy_Imap::handler(enum gse_events eve,nsock_event nse){ + char *rec; + int rec_len=0; + + switch(state){ + case GSS_INTERNAL_STATE_0: // login + switch(eve){ + case GSEE_NOEVENT: + nsock_printf(gsc->nsp, gsc->nsi, gse_generic_handler, + get_timeout(GSE_PROXY), this, + "1 LOGIN %s %s\r\n", chain->user, chain->pass); + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, + get_timeout(GSE_PROXY), this); // one line + break; + case GSEE_EOF: + case GSEE_TIMEOUT: + if(eve == GSEE_EOF) + set_reason_txt("connection closed"); + else + set_reason_txt("connection timeout"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); + if(re_answer->match(rec, rec_len) >= 0){ + char *res = re_answer->get(rec, 1); + if(o.debugging > 1) + error("IMAP: 0 %s\n",res); + if(strcmp(res, "OK")!=0){ + set_reason_txt("bad username/password: %s %s", res, re_answer->get_dup(rec, 2)); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + return; + } + state = GSS_WAITING_FOR_INPUT; + return; + } + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_PROXY), this); // one line + break; + default: + assert(0); + } + break; + case GSS_WAITING_FOR_INPUT: + assert(target_proto == IPPROTO_TCP); + HOST_resolve_simple(target_host); + assert(target_host && target_host->ip); + + state = GSS_INTERNAL_STATE_1; + handler(GSEE_NOEVENT, nse); + break; + case GSS_INTERNAL_STATE_1: // send SELECT command + switch(eve){ + case GSEE_NOEVENT: + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(), this, + "1 SELECT \"{%s:%i}\"\r\n", + HOST_tostr(target_host), + target_port); + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout()+5000, this); // one line + break; + case GSEE_EOF: + set_reason_txt("connection closed"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_TIMEOUT: + set_reason_txt("connection timeouted"); + reason = GSER_FILTERED; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); + if(re_answer->match(rec, rec_len) >= 0){ + char *res = re_answer->get(rec, 1); + char *ans = re_answer->get(rec, 2); + if(o.debugging > 1) + error("IMAP: 1 %s %s\n", res, ans); + if(strcmp(res,"NO")==0){ + if(strstr(ans,"refused")){ + reason = GSER_CLOSED; + state = GSS_WAITING_FOR_OUTPUT; + return; + }else{ + reason = GSER_OPENED; + state = GSS_WAITING_FOR_OUTPUT; + return; + } + return; + } + set_reason_txt("%s %s",res, ans); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + return; + } + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(), this); // one line + break; + default: + assert(0); + } + break; + case GSS_WAITING_FOR_OUTPUT: + if(reason == GSER_GW_ERROR || + reason == GSER_FILTERED) + state = GSS_DEAD; + else + state = GSS_WAITING_FOR_INPUT; + break; + case GSS_DEAD: + default: + assert(0); + break; + } +} + + + diff -Nraupb -I ' \$Id: ' nmap-org/gse.h nmap-new/gse.h --- nmap-org/gse.h 1970-01-01 01:00:00.000000000 +0100 +++ nmap-new/gse.h 2006-07-26 02:27:49.000000000 +0200 @@ -0,0 +1,284 @@ + +/*************************************************************************** + * gse.cc -- General Scanning Engine, asynchronous proxy chaining * + * * + ***************************************************************************/ + +#ifndef GSE_H_ +#define GSE_H_ + +#ifdef HAVE_PCRE_PCRE_H +# include +#else +# include +#endif +#include + + +/*************************************************************************** + HOST -- the storage for host addresses + ***************************************************************************/ +struct Host{ + u32 magic; + int counter; // how many times this structure is used? 0= destroy + int af; // type of 'ip' field. AF_UNKNOWN, AF_INET, AF_INET6 + u8 *ip; // ip. 4 bytes used for ipv4, 16 bytes used for ipv6. NULL if hostname not resolved + char *hostname; // not resolved hostname, what user typed + + struct sockaddr *sa; // cached 'sockaddr' structure, used by HOST_getsa() + + class Target *ths; // +}; +typedef struct Host HOST; + + +// create HOST structure +HOST *HOST_new(char *hostname); +HOST *HOST_new(char *hostname, class Target *nths); + +// copy HOST structure +HOST *HOST_copy(HOST *host); +// delete HOST structure +void HOST_delete(HOST *host); + +// get sockaddr structure from HOST +struct sockaddr *HOST_getsa(HOST *host); +size_t HOST_getsasize(HOST *host); + +// check if hostname is string with ip. no dns query will be performed of course. +void HOST_resolve_simple(HOST *host); + +// resolve HOST. +void HOST_resolve(HOST *host); +void HOST_resolve(HOST *host, bool printwarning); + +// get string that contains ip of host if possible, or hostname if no resolved ip is present +char *HOST_tostr(HOST *host); + +// is resolved ip in ipv6 ? +bool HOST_isipv6(HOST *host); + +class Target *HOST_get_ths(HOST *host); + + +/*************************************************************************** + ENGINES for gse + ***************************************************************************/ +/* Available scanning engines. Order is very important. + When changin this also remember to update + 'struct gse_engine_definitions' +*/ +enum gse_engines{ + GSEENG_UNINITIALISED = 0, + GSEENG_NSOCK = 1, + GSEENG_HTTPCONNECT = 2, + GSEENG_HTTPGET = 3, + GSEENG_SOCKS4A = 4, + GSEENG_FTPBOUNCE = 5, + GSEENG_IMAP = 6, +}; + +// Flags for scanning engines +enum gse_flags{ + GSEF_NONE = 0x00, + GSEF_DNS = 0x01, // dns can be resolved on remote side + GSEF_UDP = 0x02, // handles udp + GSEF_IPV6 = 0x04, // handles ipv6 protocol + GSEF_FIRST= 0x08, // should only be used as first in chain + GSEF_LAST = 0x10, // should only be used as last in chain +}; + + +struct gse_eng_definitions{ + enum gse_engines engine; + int flags; + void *(*create_fun)(); + char *names[16]; +}; + +extern struct gse_eng_definitions gsee_definitions[]; +extern int gsee_definitions_len; + + + + + + +/*************************************************************************** + CHIAN - general data for creating GSEProxy_* + ***************************************************************************/ + +/* General data for every proxy in the chain. This informations + are used to create 'GSEProxy_*' array. */ +struct GSE_Chain{ + u16 px_port; // port number, only TCP + HOST *px_host; // host address + + char *user; // username if needed + char *pass; // password if needed + + enum gse_engines engine; // engine type + + struct timeout_info to; // timeout for that proxy +}; +typedef struct GSE_Chain CHAIN; + +// create CHAIN array from user input +CHAIN *CHAIN_create(int *chain_len, char *text); +// delete CHAIN array +void CHAIN_delete(CHAIN *chain, int chain_len); + +// print CHAIN array (one line) +void CHAIN_print(CHAIN *chain, int chain_len); +// get string about one CHAIN +char *CHAIN_tostr(CHAIN *chain); + + + + +/*************************************************************************** + ENUMS + ***************************************************************************/ +/* Each 'proxy' in chain can have different state. When the state + is 'ALIVE' we can connect to next hop. Of course not every engine + is supporting this state. +*/ +enum gse_states{ + GSS_UNINITIALISED= 0, // state when proxy was not initialised + GSS_INTERNAL_STATE_0, + GSS_INTERNAL_STATE_1, + GSS_INTERNAL_STATE_2, + GSS_INTERNAL_STATE_3, + GSS_INTERNAL_STATE_4, + GSS_WAITING_FOR_INPUT, // we are waiting for 'querynewport()' + GSS_WAITING_FOR_OUTPUT, // we are waiting for 'getportstatus()' + GSS_DEAD, // socket needs to be closed + GSS_ALIVE, // socket is now connected to the target's port. +}; + +/* After 'getportstatus()' we receive port status. I wanted not to + confuse 'port status' with 'proxy status', so 'port status' is + called 'reason'. + Some of this reasons have the same meaning like states from 'portlist.h', + but other are more specific like 'GSER_GW_FILTERED'. +*/ +enum gse_reasons{ + GSER_NONE, + GSER_GW_ERROR, // gateway unreachable or bad credentials + GSER_GW_FILTERED, // gateway is blocking connection to this port + GSER_CLOSED, // port is closed + GSER_OPENED, // port is opened + GSER_FILTERED, // port is filtered (packets are lost) + GSER_CLOSED_FILTERED, // port is closed or filtered +}; +/* change 'GSER_*' port states to one word strings */ +char *gse_reason_tostr(enum gse_reasons reason); + + +/* Simplified events for handlers */ +enum gse_events{ + GSEE_NOEVENT, // event handler executed 'by hand', from program + GSEE_READ, // some data received + GSEE_TIMEOUT, // timeouted + GSEE_EOF, // connection broken/closed + GSEE_CONNECT, // connection successfull! +}; + + + +/*************************************************************************** + GSECONNECTON - this class is binded to exacly one nsock_iod + ***************************************************************************/ +class GSEConnection{ +private: + class GSEProxy_Interface **proxy_pool; // array of pointers + int proxy_pool_len; // how many hops do we have? + int proxy_current; // our current hop +public: + int gss_pool_off; // index of this class in pool in GSEScanner + +public: // the GSEProxy's must have access to these: + nsock_pool nsp; + nsock_iod nsi; + CHAIN *chain; // chain array. used in building hops. + +public: + bool GSEConnection::chain_built(); // is building of this chain ended? + void GSEConnection::query_nexthop(); // give current Proxy information about target (ie next hop) + void GSEConnection::jump_nexthop(); // connection to next hop established (ALIVE), move to next Proxy + + // Wrappers to current proxy in chain + enum gse_states GSEConnection::get_state(); // state of current Proxy + void GSEConnection::querynewport(HOST *host, u16 port, u8 proto);// give host to scan + struct gse_gps_tmp GSEConnection::getportstatus();// get info about scanned host + + CHAIN *get_curr_chain(); + +private: + void build_hop(int i); +public: + GSEConnection(CHAIN *nchain, int chainlen, nsock_pool nnsp, int ngss_pool_off); + ~GSEConnection(); +}; + +/*************************************************************************** + GSESCANNER - this class is binded to exacly one nsock_pool + ***************************************************************************/ +class GSEScanner{ +private: + nsock_pool nsp; + + class GSEConnection **connpool; + int connpool_len; + + u16 *portarr; + int portarr_len; + int portarr_pos; + u8 protocol; + + HOST **targetlist; + int targetlist_len; + int targetlist_pos; + + CHAIN *chain; + int chain_len; + +public: + int conn_counter; // how many connections we created + int debug; // debugging on? +public: + // how many ports left to scan? + int get_portsleft(); + // number of active connection + int get_active_conn(); + + bool get_new_target(HOST **target, u16 *port, u8 *proto); + void conn_remove(int conn_off); + void conn_give_input(int conn_off); + void conn_read_output(int conn_off); + void conn_alive(int conn_off); + bool conn_exists(int conn_off); + enum gse_states conn_get_state(int conn_off); + + GSEScanner(){}; + virtual ~GSEScanner(); + + // a'la conctructor. The main function here. + void GSEScanner_creat(int conn_max, + CHAIN *nchain, int nchainlen, + HOST **ntargetlist, int ntargetlist_len, + u16 *nportarr, int nportarrlen, u8 nprotocol); + // yep, get nsock_pool and please do nsock_loop on it. + nsock_pool get_nsp(); + + // this functions *MUST* be overloaded by you. + /* Yeah, now we know some connection result. port_state is state from 'portlist.h' (for example PORT_CLOSED)*/ + virtual void handle_result(HOST *host, u16 port, u8 proto, int port_state)=0; + + /* This connection is now created :) This is needed if you want get connection after building chain. */ + virtual void handle_alive(GSEConnection *conn)=0; +}; + + + +#endif /*GSE_H_*/ diff -Nraupb -I ' \$Id: ' nmap-org/Makefile.in nmap-new/Makefile.in --- nmap-org/Makefile.in 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/Makefile.in 2006-07-26 02:27:49.000000000 +0200 @@ -48,11 +48,11 @@ TARGET = nmap TARGETNMAPFE=@TARGETNMAPFE@ INSTALLNMAPFE=@INSTALLNMAPFE@ -export SRCS = main.cc nmap.cc targets.cc tcpip.cc nmap_error.cc utils.cc idle_scan.cc osscan.cc osscan2.cc output.cc scan_engine.cc timing.cc charpool.cc services.cc protocols.cc nmap_rpc.cc portlist.cc NmapOps.cc TargetGroup.cc Target.cc FingerPrintResults.cc service_scan.cc NmapOutputTable.cc MACLookup.cc nmap_tty.cc nmap_dns.cc @COMPAT_SRCS@ +export SRCS = main.cc nmap.cc targets.cc tcpip.cc nmap_error.cc utils.cc idle_scan.cc osscan.cc osscan2.cc output.cc scan_engine.cc timing.cc charpool.cc services.cc protocols.cc nmap_rpc.cc portlist.cc NmapOps.cc TargetGroup.cc Target.cc FingerPrintResults.cc service_scan.cc NmapOutputTable.cc MACLookup.cc nmap_tty.cc nmap_dns.cc gse.cc @COMPAT_SRCS@ -export HDRS = charpool.h FingerPrintResults.h global_structures.h idle_scan.h MACLookup.h nmap_amigaos.h nmap_dns.h nmap_error.h nmap.h NmapOps.h NmapOutputTable.h nmap_rpc.h nmap_tty.h nmap_winconfig.h osscan.h osscan2.h output.h portlist.h protocols.h scan_engine.h service_scan.h services.h TargetGroup.h Target.h targets.h tcpip.h timing.h utils.h +export HDRS = charpool.h FingerPrintResults.h global_structures.h idle_scan.h MACLookup.h nmap_amigaos.h nmap_dns.h nmap_error.h nmap.h NmapOps.h NmapOutputTable.h nmap_rpc.h nmap_tty.h nmap_winconfig.h osscan.h osscan2.h output.h portlist.h protocols.h scan_engine.h service_scan.h services.h TargetGroup.h Target.h targets.h tcpip.h timing.h utils.h gse.h -OBJS = main.o nmap.o targets.o tcpip.o nmap_error.o utils.o idle_scan.o osscan.o osscan2.o output.o scan_engine.o timing.o charpool.o services.o protocols.o nmap_rpc.o portlist.o NmapOps.o TargetGroup.o Target.o FingerPrintResults.o service_scan.o NmapOutputTable.o MACLookup.o nmap_tty.o nmap_dns.o @COMPAT_OBJS@ +OBJS = main.o nmap.o targets.o tcpip.o nmap_error.o utils.o idle_scan.o osscan.o osscan2.o output.o scan_engine.o timing.o charpool.o services.o protocols.o nmap_rpc.o portlist.o NmapOps.o TargetGroup.o Target.o FingerPrintResults.o service_scan.o NmapOutputTable.o MACLookup.o nmap_tty.o nmap_dns.o gse.o @COMPAT_OBJS@ # %.o : %.cc -- nope this is a GNU extension .cc.o: diff -Nraupb -I ' \$Id: ' nmap-org/nmap.cc nmap-new/nmap.cc --- nmap-org/nmap.cc 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/nmap.cc 2006-07-26 02:27:49.000000000 +0200 @@ -155,45 +155,6 @@ static int parse_scanflags(char *arg) { return flagval; } -/* parse a URL stype ftp string of the form user:pass@server:portno */ -static int parse_bounce_argument(struct ftpinfo *ftp, char *url) { - char *p = url,*q, *s; - - if ((q = strrchr(url, '@'))) { /*we have username and/or pass */ - *(q++) = '\0'; - if ((s = strchr(q, ':'))) - { /* has portno */ - *(s++) = '\0'; - strncpy(ftp->server_name, q, MAXHOSTNAMELEN); - ftp->port = atoi(s); - } - else strncpy(ftp->server_name, q, MAXHOSTNAMELEN); - - if ((s = strchr(p, ':'))) { /* User AND pass given */ - *(s++) = '\0'; - strncpy(ftp->user, p, 63); - strncpy(ftp->pass, s, 255); - } - else { /* Username ONLY given */ - log_write(LOG_STDOUT, "Assuming %s is a username, and using the default password: %s\n", - p, ftp->pass); - strncpy(ftp->user, p, 63); - } - } - else /* no username or password given */ - if ((s = strchr(url, ':'))) { /* portno is given */ - *(s++) = '\0'; - strncpy(ftp->server_name, url, MAXHOSTNAMELEN); - ftp->port = atoi(s); - } - else /* default case, no username, password, or portnumber */ - strncpy(ftp->server_name, url, MAXHOSTNAMELEN); - - ftp->user[63] = ftp->pass[255] = ftp->server_name[MAXHOSTNAMELEN] = 0; - - return 1; -} - static void printusage(char *name, int rc) { printf("%s %s ( %s )\n" @@ -220,7 +181,7 @@ printf("%s %s ( %s )\n" " --scanflags : Customize TCP scan flags\n" " -sI : Idlescan\n" " -sO: IP protocol scan\n" - " -b : FTP bounce scan\n" + " -x : Scan through proxies\n" "PORT SPECIFICATION AND SCAN ORDER:\n" " -p : Only scan specified ports\n" " Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080\n" @@ -450,14 +411,6 @@ int nmap_main(int argc, char *argv[]) { struct scan_lists *ports = NULL; TargetGroup *exclude_group = NULL; char myname[MAXHOSTNAMELEN + 1]; -#if (defined(IN_ADDR_DEEPSTRUCT) || defined( SOLARIS)) - /* Note that struct in_addr in solaris is 3 levels deep just to store an - * unsigned int! */ - struct ftpinfo ftp = { FTPUSER, FTPPASS, "", { { { 0 } } } , 21, 0}; -#else - struct ftpinfo ftp = { FTPUSER, FTPPASS, "", { 0 }, 21, 0}; -#endif - struct hostent *target = NULL; char **fakeargv; Target *currenths; vector Targets; @@ -474,6 +427,9 @@ int nmap_main(int argc, char *argv[]) { size_t sslen; int option_index; bool iflist = false; + char *chain_str=NULL; + int chain_len; + CHAIN *chain = NULL; // Pre-specified timing parameters. // These are stored here during the parsing of the arguments so that we can @@ -600,7 +556,7 @@ int nmap_main(int argc, char *argv[]) { /* OK, lets parse these args! */ optind = 1; /* so it can be called multiple times */ - while((arg = getopt_long_only(argc,fakeargv,"6Ab:D:d::e:Ffg:hIi:M:m:nO::o:P:p:qRrS:s:T:Vv", long_options, &option_index)) != EOF) { +while((arg = getopt_long_only(argc,fakeargv,"6Ab:D:d::e:Ffg:hIi:M:m:nO::o:P:p:qRrS:s:T:Vvx:", long_options, &option_index)) != EOF) { switch(arg) { case 0: if (optcmp(long_options[option_index].name, "max-rtt-timeout") == 0) { @@ -808,10 +764,7 @@ int nmap_main(int argc, char *argv[]) { o.osscan = OS_SCAN_DEFAULT; break; case 'b': - o.bouncescan++; - if (parse_bounce_argument(&ftp, optarg) < 0 ) { - fprintf(stderr, "Your argument to -b is b0rked. Use the normal url style: user:pass@server:port or just use server and use default anon login\n Use -h for help\n"); - } + fatal("Sorry, FTP bounce scan is now supported by '-x' option."); break; case 'D': p = optarg; @@ -996,8 +949,6 @@ int nmap_main(int argc, char *argv[]) { while(*p) { switch(*p) { case 'A': o.ackscan = 1; break; - case 'B': fatal("No scan type 'B', did you mean bounce scan (-b)?"); - break; case 'F': o.finscan = 1; break; case 'L': o.listscan = 1; o.pingtype = PINGTYPE_NONE; break; case 'M': o.maimonscan = 1; break; @@ -1058,6 +1009,9 @@ int nmap_main(int argc, char *argv[]) { exit(0); break; case 'v': o.verbose++; break; + case 'x': + o.gsescan = true; + chain_str = strdup(optarg); } } @@ -1099,6 +1053,15 @@ int nmap_main(int argc, char *argv[]) { else o.reference_FPs = parse_fingerprint_reference_file("nmap-os-db"); + if(o.gsescan){ + chain = CHAIN_create(&chain_len, chain_str); + free(chain_str); + if(o.verbose){ + printf("You selected chain:"); + CHAIN_print(chain, chain_len); + } + } + o.ValidateOptions(); /* Open the log files, now that we know whether the user wants them appended @@ -1258,21 +1221,6 @@ int nmap_main(int argc, char *argv[]) { o.setSourceSockAddr((struct sockaddr_storage *) &tmpsock, sizeof(tmpsock)); } - - /* If he wants to bounce off of an ftp site, that site better damn well be reachable! */ - if (o.bouncescan) { - if (!inet_pton(AF_INET, ftp.server_name, &ftp.server)) { - if ((target = gethostbyname(ftp.server_name))) - memcpy(&ftp.server, target->h_addr_list[0], 4); - else { - fprintf(stderr, "Failed to resolve ftp bounce proxy hostname/IP: %s\n", - ftp.server_name); - exit(1); - } - } else if (o.verbose) - log_write(LOG_STDOUT, "Resolved ftp bounce attack proxy to %s (%s).\n", - ftp.server_name, inet_ntoa(ftp.server)); - } fflush(stdout); fflush(stderr); @@ -1544,6 +1492,10 @@ int nmap_main(int argc, char *argv[]) { if (o.ipprotscan) ultra_scan(Targets, ports, IPPROT_SCAN); + if (o.gsescan){ + gse_scan(Targets, ports, chain, chain_len); + CHAIN_delete(chain, chain_len); + } /* These lame functions can only handle one target at a time */ for(targetno = 0; targetno < Targets.size(); targetno++) { currenths = Targets[targetno]; @@ -1553,13 +1505,6 @@ int nmap_main(int argc, char *argv[]) { idle_scan(currenths, ports->tcp_ports, ports->tcp_count, idleProxy); } - if (o.bouncescan) { - o.current_scantype = BOUNCE_SCAN; - keyWasPressed(); // Check if a status message should be printed - if (ftp.sd <= 0) ftp_anon_connect(&ftp); - if (ftp.sd > 0) bounce_scan(currenths, ports->tcp_ports, - ports->tcp_count, &ftp); - } } if (o.servicescan) { @@ -2090,9 +2035,9 @@ char *scantype2str(stype scantype) { case PING_SCAN: return "Ping Scan"; break; case PING_SCAN_ARP: return "ARP Ping Scan"; break; case IDLE_SCAN: return "Idle Scan"; break; - case BOUNCE_SCAN: return "Bounce Scan"; break; case SERVICE_SCAN: return "Service Scan"; break; case OS_SCAN: return "OS Scan"; break; + case GSE_SCAN: return "Proxy Scan"; break; default: assert(0); break; } @@ -2113,89 +2058,6 @@ char *statenum2str(int state) { return "unknown"; } -int ftp_anon_connect(struct ftpinfo *ftp) { - int sd; - struct sockaddr_in sock; - int res; - char recvbuf[2048]; - char command[512]; - - if (o.verbose || o.debugging) - log_write(LOG_STDOUT, "Attempting connection to ftp://%s:%s@%s:%i\n", ftp->user, ftp->pass, - ftp->server_name, ftp->port); - - if ((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { - perror("Couldn't create ftp_anon_connect socket"); - return 0; - } - - sock.sin_family = AF_INET; - sock.sin_addr.s_addr = ftp->server.s_addr; - sock.sin_port = htons(ftp->port); - res = connect(sd, (struct sockaddr *) &sock, sizeof(struct sockaddr_in)); - if (res < 0 ) { - fprintf(stderr, "Your ftp bounce proxy server won't talk to us!\n"); - exit(1); - } - if (o.verbose || o.debugging) log_write(LOG_STDOUT, "Connected:"); - while ((res = recvtime(sd, recvbuf, sizeof(recvbuf) - 1,7, NULL)) > 0) - if (o.debugging || o.verbose) { - recvbuf[res] = '\0'; - log_write(LOG_STDOUT, "%s", recvbuf); - } - if (res < 0) { - perror("recv problem from ftp bounce server"); - exit(1); - } - - snprintf(command, 511, "USER %s\r\n", ftp->user); - - send(sd, command, strlen(command), 0); - res = recvtime(sd, recvbuf, sizeof(recvbuf) - 1,12, NULL); - if (res <= 0) { - perror("recv problem from ftp bounce server"); - exit(1); - } - recvbuf[res] = '\0'; - if (o.debugging) log_write(LOG_STDOUT, "sent username, received: %s", recvbuf); - if (recvbuf[0] == '5') { - fprintf(stderr, "Your ftp bounce server doesn't like the username \"%s\"\n", - ftp->user); - exit(1); - } - - snprintf(command, 511, "PASS %s\r\n", ftp->pass); - - send(sd, command, strlen(command), 0); - res = recvtime(sd, recvbuf, sizeof(recvbuf) - 1,12, NULL); - if (res < 0) { - perror("recv problem from ftp bounce server\n"); - exit(1); - } - if (!res) fprintf(stderr, "Timeout from bounce server ..."); - else { - recvbuf[res] = '\0'; - if (o.debugging) log_write(LOG_STDOUT, "sent password, received: %s", recvbuf); - if (recvbuf[0] == '5') { - fprintf(stderr, "Your ftp bounce server refused login combo (%s/%s)\n", - ftp->user, ftp->pass); - exit(1); - } - } - while ((res = recvtime(sd, recvbuf, sizeof(recvbuf) - 1,2, NULL)) > 0) - if (o.debugging) { - recvbuf[res] = '\0'; - log_write(LOG_STDOUT, "%s", recvbuf); - } - if (res < 0) { - perror("recv problem from ftp bounce server"); - exit(1); - } - if (o.verbose) log_write(LOG_STDOUT, "Login credentials accepted by ftp server!\n"); - - ftp->sd = sd; - return sd; -} #ifndef WIN32 diff -Nraupb -I ' \$Id: ' nmap-org/nmap.h nmap-new/nmap.h --- nmap-org/nmap.h 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/nmap.h 2006-07-26 02:27:49.000000000 +0200 @@ -283,10 +283,7 @@ void *realloc(); #define FAKE_ARGV "pine" /* What ps and w should show if you use -q */ /* How do we want to log into ftp sites for */ -#define FTPUSER "anonymous" -#define FTPPASS "-wwwuser@" -#define FTP_RETRIES 2 /* How many times should we relogin if we lose control - connection? */ + #define MAX_TIMEOUTS MAX_SOCKETS /* How many timed out connection attempts in a row before we decide the host is dead? */ @@ -447,8 +444,6 @@ void printinteractiveusage(); int check_ident_port(struct in_addr target); -int ftp_anon_connect(struct ftpinfo *ftp); - /* port manipulators */ void getprobepts(char *expr); struct scan_lists *getpts(char *expr); /* someone stole the name getports()! */ diff -Nraupb -I ' \$Id: ' nmap-org/NmapOps.cc nmap-new/NmapOps.cc --- nmap-org/NmapOps.cc 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/NmapOps.cc 2006-07-26 02:27:49.000000000 +0200 @@ -226,7 +226,7 @@ void NmapOps::Initialize() { override_excludeports = 0; version_intensity = 7; pingtype = PINGTYPE_UNKNOWN; - listscan = pingscan = allowall = ackscan = bouncescan = connectscan = 0; + listscan = pingscan = allowall = ackscan = connectscan = 0; rpcscan = nullscan = xmasscan = fragscan = synscan = windowscan = 0; maimonscan = idlescan = finscan = udpscan = ipprotscan = noresolve = 0; force = append_output = 0; @@ -253,11 +253,11 @@ void NmapOps::Initialize() { noninteractive = false; current_scantype = STYPE_UNKNOWN; release_memory = false; - + gsescan = false; } bool NmapOps::TCPScan() { - return ackscan|bouncescan|connectscan|finscan|idlescan|maimonscan|nullscan|synscan|windowscan|xmasscan; + return ackscan|connectscan|finscan|idlescan|maimonscan|nullscan|synscan|windowscan|xmasscan||gsescan; } bool NmapOps::UDPScan() { @@ -392,14 +392,11 @@ void NmapOps::ValidateOptions() { error("WARNING: RPC scan currently does not make use of decoys so don't count on that protection"); } - if (bouncescan && pingtype != PINGTYPE_NONE) - log_write(LOG_STDOUT, "Hint: if your bounce scan target hosts aren't reachable from here, remember to use -P0 so we don't try and ping them prior to the scan\n"); - - if (ackscan+bouncescan+connectscan+finscan+idlescan+maimonscan+nullscan+synscan+windowscan+xmasscan > 1) - fatal("You specified more than one type of TCP scan. Please choose only one of -sA, -b, -sT, -sF, -sI, -sM, -sN, -sS, -sW, and -sX"); + if (ackscan+connectscan+finscan+idlescan+maimonscan+nullscan+synscan+windowscan+xmasscan+gsescan > 1) + fatal("You specified more than one type of TCP scan. Please choose only one of -sA, -x, -sT, -sF, -sI, -sM, -sN, -sS, -sW, and -sX"); - if (numdecoys > 0 && (bouncescan || connectscan)) { - error("WARNING: Decoys are irrelevant to the bounce or connect scans"); + if (numdecoys > 0 && (connectscan)) { + error("WARNING: Decoys are irrelevant to the connect scans"); } if (fragscan && !(ackscan|finscan|maimonscan|nullscan|synscan|windowscan|xmasscan) && \ @@ -408,9 +405,6 @@ void NmapOps::ValidateOptions() { fatal("Fragscan only works with TCP, ICMP Timestamp or ICMP Mask (mtu=8) ping types or ACK, FIN, Maimon, NULL, SYN, Window, and XMAS scan types"); } - if (osscan && bouncescan) - error("Combining bounce scan with OS scan seems silly, but I will let you do whatever you want!"); - #if !defined(LINUX) && !defined(OPENBSD) && !defined(FREEBSD) && !defined(NETBSD) if (fragscan) { fprintf(stderr, "Warning: Packet fragmentation selected on a host other than Linux, OpenBSD, FreeBSD, or NetBSD. This may or may not work.\n"); @@ -436,16 +430,41 @@ void NmapOps::ValidateOptions() { fatal("--min-parallelism=%i must be less than or equal to --max-parallelism=%i",min_parallelism,max_parallelism); } - if (af() == AF_INET6 && (numdecoys|osscan|bouncescan|fragscan|ackscan|finscan|idlescan|ipprotscan|maimonscan|nullscan|rpcscan|synscan|udpscan|windowscan|xmasscan)) { + if (af() == AF_INET6 && (numdecoys|osscan|fragscan|ackscan|finscan|idlescan|ipprotscan|maimonscan|nullscan|rpcscan|synscan|udpscan|windowscan|xmasscan||gsescan)) { fatal("Sorry -- IPv6 support is currently only available for connect() scan (-sT), ping scan (-sP), and list scan (-sL). OS detection and decoys are also not supported with IPv6. Further support is under consideration."); } + if (pingtype != PINGTYPE_NONE && gsescan) { + error("WARNING: Many people use -P0 w/Proxyscan to prevent pings from their true IP."); + sleep(3); /* Give ppl a chance for ^C :) */ + } + + if(numdecoys > 0 && gsescan){ + fatal("Your decoys won't be used in the Proxyscan"); + } + + if(osscan && gsescan){ + fatal("OS Scan is not compatible with a proxyscan"); + } + + if(servicescan && gsescan){ + fatal("Service scan is not working with a proxyscan, yet :)"); + } + + if(gsescan && !noresolve && !dns_servers){ + error("WARNING: Resolving hostnames on remote site is not supported by proxyscan. Target hostnames will be resolved LOCALLY. Use '-n' to suppress this warning."); + sleep(3); + } + if (af() != AF_INET) mass_dns = false; /* Prevent performance values from getting out of whack */ if (min_parallelism > max_parallelism) max_parallelism = min_parallelism; + if(gsescan && max_parallelism < 2){ + error("Hint: you can use '--max-parallelism' option to increase proxyscan speed"); + } } void NmapOps::setMaxRttTimeout(int rtt) diff -Nraupb -I ' \$Id: ' nmap-org/NmapOps.h nmap-new/NmapOps.h --- nmap-org/NmapOps.h 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/NmapOps.h 2006-07-26 02:27:49.000000000 +0200 @@ -268,7 +268,6 @@ class NmapOps { int allowall; int fragscan; /* 0 or MTU (without IPv4 header size) */ int ackscan; - int bouncescan; int connectscan; int finscan; int idlescan; @@ -281,6 +280,7 @@ class NmapOps { int windowscan; int xmasscan; int noresolve; + bool gsescan; int force; /* force nmap to continue on even when the outcome seems somewhat certain */ int append_output; /* Append to any output files rather than overwrite */ FILE *logfd[LOG_NUM_FILES]; @@ -302,9 +302,9 @@ class NmapOps { bool release_memory; /* suggest to release memory before quitting. used to find memory leaks. */ private: - int max_rtt_timeout; - int min_rtt_timeout; - int initial_rtt_timeout; + long max_rtt_timeout; + long min_rtt_timeout; + long initial_rtt_timeout; int max_retransmissions; unsigned int max_tcp_scan_delay; unsigned int max_udp_scan_delay; diff -Nraupb -I ' \$Id: ' nmap-org/nsock/src/nsock_event.c nmap-new/nsock/src/nsock_event.c --- nmap-org/nsock/src/nsock_event.c 2006-07-26 02:27:47.000000000 +0200 +++ nmap-new/nsock/src/nsock_event.c 2006-07-26 02:27:48.000000000 +0200 @@ -252,7 +252,7 @@ nsock_event_id get_new_event_id(mspool * unsigned long serial = ms->next_event_serial++; unsigned long max_serial_allowed; int shiftbits; - assert(type <= 3); + assert(type <= 4); shiftbits = sizeof(nsock_event_id) * 8 - TYPE_CODE_NUM_BITS; max_serial_allowed = ( 1 << shiftbits ) - 1; diff -Nraupb -I ' \$Id: ' nmap-org/nsock/src/nsock_pool.c nmap-new/nsock/src/nsock_pool.c --- nmap-org/nsock/src/nsock_pool.c 2006-07-26 02:27:47.000000000 +0200 +++ nmap-new/nsock/src/nsock_pool.c 2006-07-26 02:27:48.000000000 +0200 @@ -217,6 +217,7 @@ void nsp_delete(nsock_pool ms_pool) { } msevent_delete(nsp, nse); } + gh_list_free(event_lists[current_list_idx]); } /* Then I go through and kill the iods */ @@ -232,5 +233,12 @@ void nsp_delete(nsock_pool ms_pool) { free(nsi); } + while((nsi = (msiod *) gh_list_pop(&nsp->evl.free_events))) { + free(nsi); + } + gh_list_free(&nsp->evl.free_events); + gh_list_free(&nsp->active_iods); + gh_list_free(&nsp->free_iods); + free(nsp); } diff -Nraupb -I ' \$Id: ' nmap-org/output.cc nmap-new/output.cc --- nmap-org/output.cc 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/output.cc 2006-07-26 02:27:49.000000000 +0200 @@ -933,8 +933,6 @@ void output_xml_scaninfo_records(struct doscaninfo("syn", "tcp", scanlist->tcp_ports, scanlist->tcp_count); if (o.ackscan) doscaninfo("ack", "tcp", scanlist->tcp_ports, scanlist->tcp_count); - if (o.bouncescan) - doscaninfo("bounce", "tcp", scanlist->tcp_ports, scanlist->tcp_count); if (o.connectscan) doscaninfo("connect", "tcp", scanlist->tcp_ports, scanlist->tcp_count); if (o.nullscan) diff -Nraupb -I ' \$Id: ' nmap-org/scan_engine.cc nmap-new/scan_engine.cc --- nmap-org/scan_engine.cc 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/scan_engine.cc 2006-07-26 02:27:49.000000000 +0200 @@ -110,6 +110,9 @@ #include "timing.h" #include "NmapOps.h" #include "nmap_tty.h" + +#include "gse.h" + #include @@ -3443,137 +3446,6 @@ void ultra_scan(vector &Target USI = NULL; } -/* FTP bounce attack scan. This function is rather lame and should be - rewritten. But I don't think it is used much anyway. If I'm going to - allow FTP bounce scan, I should really allow SOCKS proxy scan. */ -void bounce_scan(Target *target, u16 *portarray, int numports, - struct ftpinfo *ftp) { - o.current_scantype = BOUNCE_SCAN; - - time_t starttime; - int res , sd = ftp->sd, i=0; - const char *t = (const char *)target->v4hostip(); - int retriesleft = FTP_RETRIES; - char recvbuf[2048]; - char targetstr[20]; - char command[512]; - char hostname[1200]; - unsigned short portno,p1,p2; - int timedout; - - if (! numports) return; /* nothing to scan for */ - - snprintf(targetstr, 20, "%d,%d,%d,%d,", UC(t[0]), UC(t[1]), UC(t[2]), UC(t[3])); - - starttime = time(NULL); - if (o.verbose || o.debugging) { - struct tm *tm = localtime(&starttime); - assert(tm); - log_write(LOG_STDOUT, "Initiating TCP ftp bounce scan against %s at %02d:%02d\n", target->NameIP(hostname, sizeof(hostname)), tm->tm_hour, tm->tm_min ); - } - for(i=0; i < numports; i++) { - - /* Check for timeout */ - if (target->timedOut(NULL)) - return; - - portno = htons(portarray[i]); - p1 = ((unsigned char *) &portno)[0]; - p2 = ((unsigned char *) &portno)[1]; - snprintf(command, 512, "PORT %s%i,%i\r\n", targetstr, p1,p2); - if (o.debugging) log_write(LOG_STDOUT, "Attempting command: %s", command); - if (send(sd, command, strlen(command), 0) < 0 ) { - perror("send in bounce_scan"); - if (retriesleft) { - if (o.verbose || o.debugging) - log_write(LOG_STDOUT, "Our ftp proxy server hung up on us! retrying\n"); - retriesleft--; - close(sd); - ftp->sd = ftp_anon_connect(ftp); - if (ftp->sd < 0) return; - sd = ftp->sd; - i--; - } - else { - fprintf(stderr, "Our socket descriptor is dead and we are out of retries. Giving up.\n"); - close(sd); - ftp->sd = -1; - return; - } - } else { /* Our send is good */ - res = recvtime(sd, recvbuf, 2048, 15, NULL); - if (res <= 0) - perror("recv problem from ftp bounce server\n"); - - else { /* our recv is good */ - recvbuf[res] = '\0'; - if (o.debugging) log_write(LOG_STDOUT, "result of port query on port %i: %s", - portarray[i], recvbuf); - if (recvbuf[0] == '5') { - if (portarray[i] > 1023) { - fprintf(stderr, "Your ftp bounce server sucks, it won't let us feed bogus ports!\n"); - exit(1); - } - else { - fprintf(stderr, "Your ftp bounce server doesn't allow privileged ports, skipping them.\n"); - while(i < numports && portarray[i] < 1024) i++; - if (!portarray[i]) { - fprintf(stderr, "And you didn't want to scan any unpriviliged ports. Giving up.\n"); - exit(1); - } - } - } - else /* Not an error message */ - if (send(sd, "LIST\r\n", 6, 0) > 0 ) { - res = recvtime(sd, recvbuf, 2048,12, &timedout); - if (res < 0) { - perror("recv problem from ftp bounce server\n"); - } else if (res == 0) { - if (timedout) - target->ports.addPort(portarray[i], IPPROTO_TCP, NULL, - PORT_FILTERED); - else target->ports.addPort(portarray[i], IPPROTO_TCP, NULL, - PORT_CLOSED); - } else { - recvbuf[res] = '\0'; - if (o.debugging) log_write(LOG_STDOUT, "result of LIST: %s", recvbuf); - if (!strncmp(recvbuf, "500", 3)) { - /* fuck, we are not aligned properly */ - if (o.verbose || o.debugging) - fprintf(stderr, "FTP command misalignment detected ... correcting.\n"); - res = recvtime(sd, recvbuf, 2048,10, NULL); - } - if (recvbuf[0] == '1' || recvbuf[0] == '2') { - target->ports.addPort(portarray[i], IPPROTO_TCP, NULL, PORT_OPEN); - if (recvbuf[0] == '1') { - res = recvtime(sd, recvbuf, 2048,5, NULL); - recvbuf[res] = '\0'; - if (res > 0) { - if (o.debugging) log_write(LOG_STDOUT, "nxt line: %s", recvbuf); - if (recvbuf[0] == '4' && recvbuf[1] == '2' && - recvbuf[2] == '6') { - target->ports.removePort(portarray[i], IPPROTO_TCP); - if (o.debugging || o.verbose) - log_write(LOG_STDOUT, "Changed my mind about port %i\n", portarray[i]); - } - } - } - } else { - /* This means the port is closed ... */ - target->ports.addPort(portarray[i], IPPROTO_TCP, NULL, PORT_CLOSED); - } - } - } - } - } - } - - if (o.debugging || o.verbose) - log_write(LOG_STDOUT, "Scanned %d ports in %ld seconds via the Bounce scan.\n", - numports, (long) time(NULL) - starttime); - return; -} - /* I want to reverse the order of all PORT_TESTING entries in the scan list -- this way if an intermediate router along the way got overloaded and dropped the last X packets, they are @@ -3927,3 +3799,104 @@ void pos_scan(Target *target, u16 *porta } return; } + + +class GSEScanner_Engine: public GSEScanner{ + void GSEScanner_Engine::handle_result(HOST *host, u16 port, u8 proto, int port_state){ + Target *hs = HOST_get_ths(host); + hs->ports.addPort(port, proto, NULL, port_state); + } + + void GSEScanner_Engine::handle_alive(GSEConnection *conn){ + conn_remove(conn->gss_pool_off); + } +}; + + + + + +void gse_scan(vector &Targets, struct scan_lists *ports, CHAIN *chain, int chain_len) +{ + nsock_pool nsp; + enum nsock_loopstatus loopst; + int targets_len = Targets.size(); + + + // change from TARGETS to HOSTS :) + HOST **hosts = (HOST**)safe_malloc(sizeof(HOST*)*targets_len); + for(int targetno = 0; targetno < targets_len; targetno++) { + Target *hs = Targets[targetno]; + HOST *hh = HOST_new((char*)hs->targetipstr(), hs); + HOST_resolve_simple(hh); + hosts[targetno] = hh; + } + + + u16 *portarr=NULL; + int portarr_len=0; + u8 protocol=0; + + assert(!ports->prot_count); + if(ports->tcp_count && ports->udp_count) + fatal("Please select tcp OR udp scan."); + if(ports->tcp_count){ + portarr = ports->tcp_ports; + portarr_len = ports->tcp_count; + protocol = IPPROTO_TCP; + }else if(ports->udp_count){ + portarr = ports->udp_ports; + portarr_len = ports->udp_count; + protocol = IPPROTO_UDP; + }else + fatal("No ports specified"); + + for(int i=1; iip){ + HOST_resolve(chain[i].px_host, i==1?false:true); // it'll print warnings + } + } + + GSEScanner *gss = new GSEScanner_Engine(); + + gss->debug = o.debugging || o.verbose; + + gss->GSEScanner_creat(MAX(o.max_parallelism,1), + chain, chain_len, + hosts, targets_len, + portarr, portarr_len, + protocol); + nsp = gss->get_nsp(); + int all_ports = portarr_len*targets_len; + do{ + int lasttrace=0, trace=0; + if((trace=o.packetTrace()) != lasttrace) { + lasttrace = trace; + nsp_settrace(nsp, trace?5:0, o.getStartTime()); + } + if(keyWasPressed()){ + error("Scanned %i/%i ports",all_ports-gss->get_portsleft()-gss->get_active_conn(), all_ports); + } + loopst = nsock_loop(nsp, 1000); +// if(loopst == NSOCK_LOOP_TIMEOUT) +// printf("Tick :)\n"); + if(loopst == NSOCK_LOOP_ERROR){ + int err = nsp_geterrorcode(nsp); + fatal("Unexpected nsock_loop error. Error code %d (%s)", err, strerror(err)); + } + }while(loopst != NSOCK_LOOP_NOEVENTS); + + if(o.verbose || o.debugging) + error("Scan sucessfully completed. Scanned %i port%s using %i connections.", all_ports, all_ports==1?" ":"s", gss->conn_counter); + + delete gss; + + for(int targetno = 0; targetno < targets_len; targetno++) + HOST_delete(hosts[targetno]); + free(hosts); +} + + + diff -Nraupb -I ' \$Id: ' nmap-org/scan_engine.h nmap-new/scan_engine.h --- nmap-org/scan_engine.h 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/scan_engine.h 2006-07-26 02:27:49.000000000 +0200 @@ -106,6 +106,7 @@ #include "portlist.h" #include "tcpip.h" #include "global_structures.h" +#include "gse.h" /* 3rd generation Nmap scanning function. Handles most Nmap port scan types */ void ultra_scan(std::vector &Targets, struct scan_lists *ports, @@ -116,12 +117,6 @@ void ultra_scan(std::vector &T SYN Scan, Connect Scan, RPC scan, Window Scan, and ACK scan */ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype); -/* FTP bounce attack scan. This function is rather lame and should be - rewritten. But I don't think it is used much anyway. If I'm going to - allow FTP bounce scan, I should really allow SOCKS proxy scan. */ -void bounce_scan(Target *target, u16 *portarray, int numports, - struct ftpinfo *ftp); - /* Handles the scan types where no positive-acknowledgement of open port is received (those scans are in pos_scan). Super_scan includes scans such as FIN/XMAS/NULL/Maimon/UDP and IP Proto scans */ @@ -139,4 +134,9 @@ void super_scan(Target *target, u16 *por int determineScanGroupSize(int hosts_scanned_so_far, struct scan_lists *ports); + +/* Launch General Scanning Engine scan. */ +void gse_scan(std::vector &Targets, struct scan_lists *ports, CHAIN *chain, int chain_len); + + #endif /* SCAN_ENGINE_H */ diff -Nraupb -I ' \$Id: ' nmap-org/timing.cc nmap-new/timing.cc --- nmap-org/timing.cc 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/timing.cc 2006-07-26 02:27:49.000000000 +0200 @@ -130,14 +130,19 @@ void adjust_timeouts(struct timeval sent the current time */ void adjust_timeouts2(const struct timeval *sent, const struct timeval *received, - struct timeout_info *to) { - long delta = 0; + struct timeout_info *to) +{ + long delta = TIMEVAL_SUBTRACT(*received, *sent); + adjust_timeouts3(delta, to); +} + +/* Adjust our timeout values based on the time delay specified by user */ +void adjust_timeouts3(long delta, struct timeout_info *to) { if (o.debugging > 3) { log_write(LOG_STDOUT, "Timeout vals: srtt: %d rttvar: %d to: %d ", to->srtt, to->rttvar, to->timeout); } - delta = TIMEVAL_SUBTRACT(*received, *sent); /* Argh ... pcap receive time is sometimes a little off my getimeofday() results on various platforms :(. So a packet may diff -Nraupb -I ' \$Id: ' nmap-org/timing.h nmap-new/timing.h --- nmap-org/timing.h 2006-07-26 02:27:48.000000000 +0200 +++ nmap-new/timing.h 2006-07-26 02:27:49.000000000 +0200 @@ -123,6 +123,8 @@ void adjust_timeouts2(const struct timev response. We update our RTT averages, etc. */ void adjust_timeouts(struct timeval sent, struct timeout_info *to); +/* Adjust our timeout values based on the time delay specified by user */ +void adjust_timeouts3(long delta, struct timeout_info *to); /* Sleeps if necessary to ensure that it isn't called twice within less time than o.send_delay. If it is passed a non-null tv, the POST-SLEEP