diff -Nraupb nmap-4.20ALPHA4-orig/global_structures.h nmap-4.20ALPHA4-gsebeta2/global_structures.h --- nmap-4.20ALPHA4-orig/global_structures.h 2006-06-25 04:02:23.000000000 +0200 +++ nmap-4.20ALPHA4-gsebeta2/global_structures.h 2006-08-12 09:32:26.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 nmap-4.20ALPHA4-orig/gse.cc nmap-4.20ALPHA4-gsebeta2/gse.cc --- nmap-4.20ALPHA4-orig/gse.cc 1970-01-01 01:00:00.000000000 +0100 +++ nmap-4.20ALPHA4-gsebeta2/gse.cc 2006-08-12 09:32:26.000000000 +0200 @@ -0,0 +1,2847 @@ + +/*************************************************************************** + * gse.cc -- General Scanning Engine, asynchronous proxy chaining * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * * + * The Nmap Security Scanner is (C) 1996-2004 Insecure.Com LLC. Nmap * + * is also a registered trademark of Insecure.Com LLC. This program is * + * free software; you may redistribute and/or modify it under the * + * terms of the GNU General Public License as published by the Free * + * Software Foundation; Version 2. This guarantees your right to use, * + * modify, and redistribute this software under certain conditions. If * + * you wish to embed Nmap technology into proprietary software, we may be * + * willing to sell alternative licenses (contact sales@insecure.com). * + * Many security scanner vendors already license Nmap technology such as * + * our remote OS fingerprinting database and code, service/version * + * detection system, and port scanning code. * + * * + * Note that the GPL places important restrictions on "derived works", yet * + * it does not provide a detailed definition of that term. To avoid * + * misunderstandings, we consider an application to constitute a * + * "derivative work" for the purpose of this license if it does any of the * + * following: * + * o Integrates source code from Nmap * + * o Reads or includes Nmap copyrighted data files, such as * + * nmap-os-fingerprints or nmap-service-probes. * + * o Executes Nmap and parses the results (as opposed to typical shell or * + * execution-menu apps, which simply display raw Nmap output and so are * + * not derivative works.) * + * o Integrates/includes/aggregates Nmap into a proprietary executable * + * installer, such as those produced by InstallShield. * + * o Links to a library or executes a program that does any of the above * + * * + * The term "Nmap" should be taken to also include any portions or derived * + * works of Nmap. This list is not exclusive, but is just meant to * + * clarify our interpretation of derived works with some common examples. * + * These restrictions only apply when you actually redistribute Nmap. For * + * example, nothing stops you from writing and selling a proprietary * + * front-end to Nmap. Just distribute it by itself, and point people to * + * http://www.insecure.org/nmap/ to download Nmap. * + * * + * We don't consider these to be added restrictions on top of the GPL, but * + * just a clarification of how we interpret "derived works" as it applies * + * to our GPL-licensed Nmap product. This is similar to the way Linus * + * Torvalds has announced his interpretation of how "derived works" * + * applies to Linux kernel modules. Our interpretation refers only to * + * Nmap - we don't speak for any other GPL products. * + * * + * If you have any questions about the GPL licensing restrictions on using * + * Nmap in non-GPL works, we would be happy to help. As mentioned above, * + * we also offer alternative license to integrate Nmap into proprietary * + * applications and appliances. These contracts have been sold to many * + * security vendors, and generally include a perpetual license as well as * + * providing for priority support and updates as well as helping to fund * + * the continued development of Nmap technology. Please email * + * sales@insecure.com for further information. * + * * + * As a special exception to the GPL terms, Insecure.Com LLC grants * + * permission to link the code of this program with any version of the * + * OpenSSL library which is distributed under a license identical to that * + * listed in the included Copying.OpenSSL file, and distribute linked * + * combinations including the two. You must obey the GNU GPL in all * + * respects for all of the code used other than OpenSSL. If you modify * + * this file, you may extend this exception to your version of the file, * + * but you are not obligated to do so. * + * * + * If you received these files with a written license agreement or * + * contract stating terms other than the terms above, then that * + * alternative license agreement takes precedence over these comments. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes (none * + * have been found so far). * + * * + * Source code also allows you to port Nmap to new platforms, fix bugs, * + * and add new features. You are highly encouraged to send your changes * + * to fyodor@insecure.org for possible incorporation into the main * + * distribution. By sending these changes to Fyodor or one the * + * Insecure.Org development mailing lists, it is assumed that you are * + * offering Fyodor and Insecure.Com LLC the unlimited, non-exclusive right * + * to reuse, modify, and relicense the code. Nmap will always be * + * available Open Source, but this is important because the inability to * + * relicense code has caused devastating problems for other Free Software * + * projects (such as KDE and NASM). We also occasionally relicense the * + * code to third parties as discussed above. If you wish to specify * + * special license conditions of your contributions, just say so when you * + * send them. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details at * + * http://www.gnu.org/copyleft/gpl.html , or in the COPYING file included * + * with Nmap. * + * * + ***************************************************************************/ + +/* $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 adjust_timeouts3(long delta, struct timeout_info *to); // from timing.h +char *cstring_unescape(char *str, unsigned int *len); // from utils.h +int b64enc(char *str64, int str64_len, u8 *src, int src_len); // from utils.h + +extern NmapOps o; // used only in HOST_resolve. and for o.debugging/o.verbose + + +using namespace std; + +/*************************************************************************** + Local definitions + ***************************************************************************/ + +// this means that o.debugging must be above this number to display verbose debugging messages +#define GSEDEBUGGING 1 + + +/************* PCRE *****************/ +/* Simple helper for PCRE library. It's created to make + playing with regular expressions a bit easier. */ +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 + * next_line- if you use PCRE_MULTILINE you can scan one 'text' many times (once for each line) + * it should be false only for first match on the string. For next matches set it to 'true'. + * Returns: negative if not mached, 0 or more if matched successfully */ + int match(char *text, int text_len, bool next_line); + + /* If 'match()' was successful you can get substrings using these commands. + * text - quieried string + * i - number of substring (beginning from 1)*/ + char *get(char *text, int i); // don't 'free' result. It'll be done automatically. + + ~Pcre(); +private: + /* Free allocated strings. */ + 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 :) + They buffer results and return them in parts you requested. + + It is for you when you need to iterate through lines on + data received from nsock. + + Using this is of course slower than using normal nsock, but + this wrapper really makes programing nsock easier. + + TODO: This implementation uses 'memcpy'. It is possible to + create implementation without copying the same data many times. + Maybe I'll code it in the future. + TODO: Oh yes. This implementation uses . It's not + 'the fastest possible way'. Integrating this code to real nsock + will make this code even faster, because we could ommit searches + in . +*/ + +// Request exacly 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. +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); + +// Remove data that is waiting for this nsi, but don't free internal nsockbuf structures. +void nsockbuf_flush(nsock_iod nsi); + +// Your handler can receive real event or just faked. You should distinguish them +// when you update your timers :) +bool nsockbuf_fake_read(nsock_event nse); + + +/*************/ +/* Other nsock's hacks: */ +// How many microseconds went from setting this event till receiving it? +long nsockbuf_geteventtime(nsock_event vnse); + +// Brutally change nsocks_event type and nsi. +// This is used to force change of TIMER to READ event. +void nsockbuf_event_change_type(nsock_event nse, enum nse_type type, nsock_iod nsi); + + + +/************* GSEPROXYINTERFACE *****************/ +/* + * Every scanning engine must inherit from GSEProxy_Interface class. + * GSEProxy_Interface provides usefull functions which help in building + * engine class. + * As a result, each engine must provide only *one* function :) (except a'la constructor function) + * + * The most important function in engine is 'hanlder()'. You must + * provide there suitable 'states' and transitions between this states. + * + * Most of 'handler' implementations may seem compicated at first look, but + * they are really simplest implementations of state charts (which I tried to provide). + * + * You don't have to know the internals of this class to create new engine. + */ + +// Temporary structure used while receiving data from 'getportstatus()' +struct gse_gps_tmp{ + HOST *host; + u16 port; + u8 protocol; + enum gse_reasons reason; + char *reason_txt; +}; + +// See get_timeout comment. +enum gse_timeout_owner{GSE_TARGET,GSE_PROXY,GSE_PROXY_TRUE}; + +class GSEProxy_Interface{ +protected: + // Target's data. We are trying to connect to this guy. + 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,closed,... ? +private: + char *reason_txt; // error description. (malloced) + int reuse_counter; // how many times connection was reused. + // I mean: how many times this connection was used to scan + // target's port. Sometimes it's possible to scan many ports using one connection. + +public: + enum gse_timeout_owner last_gto; // During last 'read' which timer we used. It's used while updating timers. + + CHAIN *chain; // Pointer to chain entry. It's the same as gsc->chain[] + class GSEConnection *gsc;// my parent's connection +public: + enum gse_states state; /* Our current state. */ + + /* Get static string with target name and port number. */ + char *target_tostr(); + +public: + /* THIS ONE MUST BE OVERLOADED */ + virtual void handler(enum gse_events event,nsock_event nse); + + +public: + /* Can be executed only in state GSS_WAITING_FOR_INPUT. + Ask for new port. First it sets target_*, than it runs handler */ + void querynewport(HOST *host, u16 port, u8 proto); + + /* Can be executed only in state GSS_WAITING_FOR_OUTPUT. + Return state of last scanned port. It runs handler before. */ + struct gse_gps_tmp getportstatus(); + + /* Constructor :) It's not normal because constructors couldn't be inherited. */ + void GSEProxy_Int_constr(GSEConnection *ngsc, CHAIN *nchain); + + /* Set error message. This can be also information why port was classified + * as such 'reason'. For example if you classified port as 'FILTERED', you can set this + * information to 'timeout received'. But no one says this message will be used :) */ + void set_reason_txt(const char *fmt, ...); + + /* Select whether we should use PROXY or TARGET'S timer. + * If you select PROXY you will receive quite big 'o.maxRttTimeout()'. + * It's possible to receive true proxy timeout (GSE_PROXY_TRUE) but don't rely + * on that! I don't want to break trasmission because timeout was too small! + * You should know, that value of last timeout is recorded in 'last_gto'. + * When READ event occurs that value is used to identify object which will + * have updated timer. For example after you select GSE_TARGET, during next 'READ' + * event your target_host->to will be updated. And after GSE_PROXY your chain->timeout will be updated. */ + long get_timeout(enum gse_timeout_owner gto); + + /* During handling event get pointer to timeout_info structure that should be updated */ + struct timeout_info *get_timeout_info(); + + /* Increase reuse_counter. We use the same connection to scan more than one port. */ + void increase_reuse_counter(); + /* Can this connection be reused one more time? */ + bool can_be_reused(); + + // Virtual destructor. + virtual ~GSEProxy_Interface(); +}; + + +/************* ENGINES *****************/ +/* Pseudo constructors for 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(); +void *GSEProxy_ImapS_new(); + +/************* ENGINE LIST *****************/ +/* Detailed list of all possible engines. See gse.h for details. */ +/* Remember to update also 'enum gse_engines' */ +struct gse_eng_definitions gsee_definitions[] = { +{GSEENG_UNINITIALISED,GSEF_NONE, 0, NULL, {NULL} }, +{GSEENG_NSOCK, GSEF_UDP|GSEF_IPV6|GSEF_FIRST,0, GSEProxy_NSock_new, {"nsock",NULL} }, +{GSEENG_HTTPCONNECT, GSEF_DNS|GSEF_IPV6, 8080, GSEProxy_HttpConnect_new,{"httpconnect", "httpc", "hc", NULL} }, +{GSEENG_HTTPGET, GSEF_DNS|GSEF_IPV6|GSEF_LAST, 8080, GSEProxy_HttpGet_new, {"httpget", "httpg", "hg", NULL} }, +{GSEENG_SOCKS4A, GSEF_DNS, 9050, GSEProxy_Socks4a_new, {"socks4a", "socks4", "s4", "s4a", NULL} }, +{GSEENG_FTPBOUNCE, GSEF_LAST, 21, GSEProxy_FtpBounce_new, {"ftpbounce", "ftpb", "ftp", "fb", NULL} }, +{GSEENG_IMAP, GSEF_LAST, 143, GSEProxy_Imap_new, {"imap", "im", NULL} }, +{GSEENG_IMAPS, GSEF_LAST, 993, GSEProxy_ImapS_new, {"imaps", "imapssl", "is", NULL} }, +}; + +/* Number of elements in gsee_definitions */ +int gsee_definitions_len = sizeof(gsee_definitions)/sizeof(struct gse_eng_definitions); + + +/*************************************************************************** + The code + +***************************************************************************/ +/* General nsocks handler for GSE. This is indeed very important function. + It sends events to 'proxy handlers' and then runs GSEConnection:update_proxy_pool. */ +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); + + struct timeout_info *to; + long before, after; + + // run handler if needed. + switch(status){ + case NSE_STATUS_SUCCESS: + switch(type){ + case NSE_TYPE_CONNECT: + case NSE_TYPE_CONNECT_SSL: + to = gpi->get_timeout_info(); + before = to->timeout/1000; + adjust_timeouts3(nsockbuf_geteventtime(nse), to); + after = to->timeout/1000; + if(o.debugging > 1 && after!=before) + printf("timeout for %s changed from %lims to %lims %s (connect)\n", + gpi->last_gto == GSE_TARGET? "target" : "proxy", + before, after, + gpi->target_tostr()); + gpi->handler(GSEE_CONNECT, nse); + break; + case NSE_TYPE_READ: + /* If this event is faked by nsockbuf, we don't need any timeouts from it! */ + if(!nsockbuf_fake_read(nse)){ + to = gpi->get_timeout_info(); + before = to->timeout/1000; + adjust_timeouts3(nsockbuf_geteventtime(nse), to); + after = to->timeout/1000; + if(o.debugging > 1 && after!=before) + printf("timeout for %s changed from %lims to %lims %s (read)\n", + gpi->last_gto == GSE_TARGET? "target" : "proxy", + before, after, + gpi->target_tostr()); + } + 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->target_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); + } + + /* 'state' of current proxy could have been changed. Handle this. + * It's possible that GSEConnection will request to be deleted here. + */ + GSEConnection *gsc = gpi->gsc; + bool gsc_delete = gsc->update_proxy_pool(); + if(gsc_delete){ + delete(gsc); + } +} + + +/* Change 'GSER_*' port states to one word strings */ +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"); +} + + +/* Create CHAIN array from user input. + * The input must be in format: + * "engine:[//][user:pass@]host[:port]" + * many proxies must be separated by space. + * You can't use '@' or ':' in user, pass or host field. + * Please use '\xAB' hex notation if you need to specify those characters. + */ +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)); + chain[0].engine = GSEENG_NSOCK; + + + /* regexp this! */ + int ln; + for(ln=0; lnmatch(text, strlen(text), ln==0 ? false : true); + if(rc == 0) // not matched + break; + + /* parse results */ + if(rc != 7 && rc != 8){ + error("Is your specification of scanning engine really in format: 'type://[user:pass@]host[:port]' ?" + " Availble scanning engines:"); + for(int i=2; iget(text, 1); + user = chain_re->get(text, 4); + pass = chain_re->get(text, 5); + host = chain_re->get(text, 6); + if(host[0] == '['){ // host selected in '[:::]' ipv6 format, remove first and last char + int l = strlen(host); + int i; + for(i=0; iget(text, 7); + else + port = NULL; + + /* 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)); + if(port) + chain[ln+1].px_port = atoi(&port[1]); // first char is ':' + else + chain[ln+1].px_port = gsee_definitions[e].default_port; + + HOST_resolve_simple(chain[ln+1].px_host); // is proxy selected by ip? + if(ln != 0 && + !chain[ln+1].px_host->ip && // no ip address selected + !(gsee_definitions[chain[ln].engine].flags & GSEF_DNS))// and we don't support dns resolving on remote side + { + // warn if we'd have to resolve hostname locally + if(o.verbose || o.debugging) + error("WARNING: Host '%s' will be resolved on LOCAL side, not remote", HOST_tostr(chain[ln+1].px_host)); + if(o.noresolve) + fatal("You selected '-n' but proxy '%s' doesn't support dns resolving. I don't know how to resolve '%s'.\n", + CHAIN_tostr(&chain[ln]), strdup(HOST_tostr(chain[ln+1].px_host))); + HOST_resolve(chain[ln+1].px_host); + } + if(ln == 0 && !chain[ln+1].px_host->ip){ + // no warning, sorry + HOST_resolve(chain[ln+1].px_host, false); + } + if(HOST_isipv6(chain[ln+1].px_host) && + !(gsee_definitions[chain[ln].engine].flags & GSEF_IPV6)) + fatal("Sorry. Engine '%s' doesn't allow scanning ipv6 addresses. I don't know what to do with '%s'.", + CHAIN_tostr(&chain[ln]), strdup(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); +} + +/* Get static string that describes this chain. */ +char *CHAIN_getallstr(CHAIN *chain, int chain_len){ + static char b[256]; + char c[256]; + b[0]='\0'; + for(int i=1; i= 2); + if(gsee_definitions[chain[chain_len-1].engine].flags & GSEF_LAST) + return(false); // last engine must be as LAST in chain, this means we can't create tunnel through it + return(true); +} + +// Is it possible to create UDP scans using this chain? +bool CHAIN_can_do_udp(CHAIN *chain, int chain_len){ + assert(chain_len >= 2); + if(gsee_definitions[chain[chain_len-1].engine].flags & GSEF_UDP) + return(true); // last engine must support UDP + return(false); +} + +// Get static string to one CHAIN structure. +// Output string will always be in format 'engine://[user:pass]@host:port' +char *CHAIN_tostr(CHAIN *chain){ + static char a[128]; + snprintf(a,sizeof(a),"%s://%s:%i", + gsee_definitions[chain->engine].names[0], + HOST_tostr(chain->px_host), chain->px_port); + return(a); +} + + + +/*************************************************************************/ + +/* has building of this chain ended? (are we currently at the last hop) */ +bool GSEConnection::chain_built(){ + if(chain_pos == chain_len) + return(true); + return(false); +} + +/* Move chain_pos to next proxy and replace proxy_current with next hop */ +void GSEConnection::build_hop(){ + assert(chain_posGSEProxy_Int_constr(this, &chain[chain_pos]); + + chain_pos++; +} + +/* Constructor. You must specify CHAIN, nsp, nsi, GSEScanner */ +GSEConnection::GSEConnection(CHAIN *nchain, int nchain_len, nsock_pool nnsp, nsock_iod nnsi, + class GSEScanner_Interface *ngss) +{ + assert(nchain); + assert(nchain_len > 0); + assert(nnsp); + assert(nnsi); + assert(ngss); + + chain = nchain; + chain_len = nchain_len; + chain_pos = 0; + nsp = nnsp; + nsi = nnsi; + gss = ngss; + proxy_current = NULL; + build_hop(); + // run Proxy->handler for the first time. That's better way than running 'handler' by hand. + nsock_timer_create(nsp, gse_generic_handler, 0, proxy_current); +} + +GSEConnection::~GSEConnection(){ + if(proxy_current) + delete proxy_current; + if(nsi){ + nsockbuf_free(nsi); + nsi_delete(nsi, NSOCK_PENDING_ERROR); + } +} + + +/* This function is runned by asynchronous nsock handler. + * 'state' in current proxy could be changed (after async event). + * It can be requesting ports to scan, it can inform us that + * connection has been established, etc. Handle this. + * Return value true mens that this object is to be deleted. */ +bool GSEConnection::update_proxy_pool(){ + bool sth_changed=true; + /* Repeat checking state until we're sure that he's not waiting for us. */ + while(sth_changed){ + switch(proxy_current->state){ + /* + ** Proxy says that connection is dead. Hmm, that means that WE (GSEConnection) + ** are dead also! The best sollution would be to do 'delete this', but I don't + ** think that this is clean :) + */ + case GSS_DEAD: + gss->handle_remove(this); + return(true); + break; + /* + ** Proxy is waiting for target specification. + ** We can give him information about next hop, or target. + */ + case GSS_WAITING_FOR_INPUT: + if(conn_write_input()) + return(true); + break; + /* + ** Proxy has data for us. Print it or do something. + */ + case GSS_WAITING_FOR_OUTPUT: + conn_read_output(); + break; + /* + ** Yahoo, connection to target established! + */ + case GSS_ALIVE: + if(conn_alive()) + return(true); + break; + // any other option -> not our business + default: + sth_changed = false; + } // switch + } // while + return(false); //don't delete us :) +} + +bool GSEConnection::conn_write_input(){ + // Are we in the last hop? + if(chain_built()==true){ // last hop -> query for target! + // some ports left to scan? + HOST *target; + u16 port; + u8 proto; + // receive port information from GSEScanner + if(gss->handle_get_new_target(&target, &port, &proto)==true){ + if(o.debugging>=3) + error("WARNING: connection #%lu: succeded, scanning host %s:%i/%s", + nsi_id(nsi), HOST_tostr(target) , port, proto==IPPROTO_TCP?"tcp":"udp"); + proxy_current->querynewport(target, + port, + proto); + }else{ // no ports left + if(o.debugging>=3) + error("WARNING: connection #%lu: succeded, no more ports to scan. removing", + nsi_id(nsi)); + gss->handle_remove(this); + return(true); // we must die + } + }else{ // middle hop + if(o.debugging>=3) + error("WARNING: Connection #%lu: connecting to next hop %s", + nsi_id(nsi), CHAIN_tostr(&chain[chain_pos])); + assert(chain_pos < chain_len); + proxy_current->querynewport(chain[chain_pos].px_host, // give him info about next hop + chain[chain_pos].px_port, + IPPROTO_TCP); + } + return(false); +} + +void GSEConnection::conn_read_output(){ + struct gse_gps_tmp ggt; + + // read the status. + ggt = proxy_current->getportstatus(); + if(ggt.reason == GSER_GW_ERROR) + fatal("ERROR: #%lu: %s:%s", nsi_id(nsi), + gse_reason_tostr(ggt.reason), ggt.reason_txt); + + if(chain_built()){ // is event from the last hop? + 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(o.debugging>=3) + error(" #%lu: Target %s:%i/%s is %s:%s ", nsi_id(nsi), + HOST_tostr(ggt.host), ggt.port, ggt.protocol==IPPROTO_TCP?"tcp":"udp", + gse_reason_tostr(ggt.reason), ggt.reason_txt); + gss->handle_result(ggt.host, ggt.port, ggt.protocol, ps); + }else{ // middle hop, and the state isn't OPENED, that's not good + if(ggt.reason != GSER_OPENED){ + error("ERROR: #%lu: %s:%s",nsi_id(nsi), + gse_reason_tostr(ggt.reason), ggt.reason_txt); + fatal("ERROR: Cannot build a chain!"); + } + } +} + +bool GSEConnection::conn_alive(){ + // is event from the last hop? + if(chain_built()){ // last hop -> just kill connection + if(o.debugging>=3) + error("WARNING: Connection #%lu: alive", nsi_id(nsi)); + if(o.debugging > 3 && !nsockbuf_empty(nsi)) + error("nsockbuf: Flushing not empty NSI!"); + nsockbuf_flush(nsi); + nsockbuf_free(nsi); + // now the power on this connection (nsi) has GSS. + // he will decide if connection will be closed or used. + // this class should be 'deleted' as soon as possible. (but without closing socket) + gss->handle_alive(nsi); + nsi = NULL; + return(true); + } + // middle hop, go to the next one :) + build_hop(); + return(false); +} + + + + + + +/*************************************************************************/ +/* Constructor. + * re - your regular expression + * re_opts - pcre options like PCRE_MULTILINE or PCRE_CASELESS. see man 3 pcreapi for more info */ +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; +} + +/* Match line against regular expression. + * text - string queried + * text_len - string length + * next_line- if you use PCRE_MULTILINE you can scan one 'text' many times (once for each line) + * it should be false only for first match on the string. For next matches set it to 'true'. + * Returns: negative if not mached, 0 or more if matched successfully */ +int Pcre::match(char *text, int text_len, bool next_line){ + assert(text); + assert(text_len); + flush(); + if(next_line == false){ // first line! + // remove bad chars from input string. + for(int i=0; imagic = HOSTMAGIC; + h->counter = 1; + h->hostname = strdup(hname); + h->ths = NULL; + return(h); +} + +// Constructors for HOST structure +HOST *HOST_new(char *hostname, class Target *nths){ + HOST *host = HOST_new(hostname); + host->ths = nths; + return(host); +} + +// Constructors for HOST structure +HOST *HOST_new_sockaddr(struct sockaddr *ss, size_t sslen){ + struct sockaddr_in *sin = (sockaddr_in *)ss; + struct sockaddr_in6 *sin6 = (sockaddr_in6 *)ss; + + char tmp[128]; + if(sin->sin_family == AF_INET){ + inet_ntop(sin->sin_family, &sin->sin_addr, tmp, sizeof(tmp)); + }else if(sin6->sin6_family == AF_INET6){ + inet_ntop(sin6->sin6_family, &sin6->sin6_addr, tmp, sizeof(tmp)); + }else + assert(0); + + HOST *host = HOST_new(tmp); + return(host); +} + +// Copy HOST structure +HOST *HOST_copy(HOST *host){ + assert(host && host->magic==HOSTMAGIC); + host->counter++; + return(host); +} + +// Delete HOST structure +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); + } +} + + +// Get sockaddr structure from 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_zalloc(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_zalloc(sizeof(struct sockaddr_in6)); + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, host->ip, 16); + host->sa = (struct sockaddr*)sin6; + } + return(host->sa); +}; + +// Get sockaddr structure length from HOST +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); +} + + +// Check if hostname is string with ip. No dns query will be performed of course. +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. +} + +// Resolve HOST. Dns query will be performed if nessesary +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); +} + +/* Get static string that contains ip of host if possible, + or hostname if no resolved ip is present. + If resolved ip is present in ipv6 format, the result is + ipv6 address captured in square brackets. */ +char *HOST_tostr(HOST *host){ + if(!host) + return(""); + + assert(host->magic==HOSTMAGIC); + + if(host->ip){ + char ip_raw[64]; + static char ip_bra[64]; + + assert(host->af==AF_INET || host->af==AF_INET6); + if(inet_ntop(host->af, host->ip, ip_raw, sizeof(ip_raw))==NULL) + assert(0); + if(host->af == AF_INET6) + snprintf(ip_bra, sizeof(ip_bra), "[%s]",ip_raw); + else + strncpy(ip_bra, ip_raw, sizeof(ip_bra)); + return(ip_bra); + } + return(host->hostname); +} + +// Is resolved ip in ipv6 ? +bool HOST_isipv6(HOST *host){ + assert(host && host->magic==HOSTMAGIC); + + if(!host->ip) + return(false); + + if(host->af == AF_INET6) + return(true); + return(false); +} + + +// Get Target class that is linked to this HOST +class Target *HOST_get_ths(HOST *host){ + assert(host); + return(host->ths); +} + + +/*************************************************************************/ + +/* This handler is used to receive async nsocks responses */ +void nsockbuf_handler(nsock_pool nsp, nsock_event nse, void *ud); + + +struct nsockbuf_priv{ + int bytes_requested; // Have user requested bytes? + int lines_requested; // Have user requested line? + + 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; + int timeout_msecs; + + bool fake_read; // Is current read faked? +}; + + +/* This mapps nsi with nsbp. */ +map nsockbuf_map; +/* // currently unused +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); + } + } +} +*/ + +// Remove data that is waiting for this nsi, but don't free internal nsockbuf structures. +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; + } +} + +// Free buffers for this nsi, I don't need it any more. +void nsockbuf_free(nsock_iod nsi){ + assert(nsi); + struct nsockbuf_priv *nsbp = nsockbuf_map[nsi]; + if(!nsbp) + return; + + if(nsbp->bytes_requested!=0 || nsbp->lines_requested!=0) + if(o.debugging>GSEDEBUGGING+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); +} + + +// Request exacly one line. +nsock_event_id nsockbuf_readline(nsock_pool nsp, nsock_iod nsi, + nsock_ev_handler handler, int timeout_msecs, + void *userdata) +{ + nsock_event_id nse; + struct nsockbuf_priv *nsbp = nsockbuf_map[nsi]; + if(!nsbp){ // create nsbp + nsbp = (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; + nsbp->timeout_msecs = timeout_msecs; + + if( nsbp->buf_len && memchr(nsbp->buf, '\n', nsbp->buf_len) ) // already have data! + nse = nsock_timer_create(nsbp->nsp, nsockbuf_handler, 0, nsbp); + else + nse = nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, timeout_msecs, nsbp); + return(nse); +} + +// Request exact number of bytes. +nsock_event_id nsockbuf_readbytes(nsock_pool nsp, nsock_iod nsi, + nsock_ev_handler handler, int timeout_msecs, + void *userdata, int nbytes) +{ + nsock_event_id nse; + struct nsockbuf_priv *nsbp = nsockbuf_map[nsi]; + if(!nsbp){ // create nsbp + nsbp = (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; + nsbp->timeout_msecs = timeout_msecs; + + if( nsbp->buf && nsbp->buf_len>=nsbp->bytes_requested ) // already have data! + nse = nsock_timer_create(nsbp->nsp, nsockbuf_handler, 0, nsbp); + else + nse = nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, timeout_msecs, nsbp); + return(nse); +} + +// helper for nsockbuf_readbuf +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); + } + // nsockbuf_getlines called but no data availble + *rec_len = 0; + return(NULL); +} + +// helper for nsockbuf_readbuf +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); + } + + // nsockbuf_getbytes called but no data availble + *rec_len = 0; + return(NULL); +} + +// Receive what you requested. +char *nsockbuf_readbuf(nsock_event nse, int *nbytes){ + nsock_iod nsi = nse_iod(nse); + assert(nsi); + + struct nsockbuf_priv *nsbp; + nsbp = nsockbuf_map[nsi]; + assert(nsbp); + + + 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); +} + + +// Your handler can receive real event or just faked. You should distinguish them +// when you update your timers :) +bool nsockbuf_fake_read(nsock_event nse){ + if(!nse) + return(false); + + nsock_iod nsi = nse_iod(nse); + if(!nsi) + return(false); + + struct nsockbuf_priv *nsbp; + nsbp = nsockbuf_map[nsi]; + if(!nsbp) + return(false); + + return(nsbp->fake_read); +} + + +/* hanles nsocks async responses */ +void nsockbuf_handler(nsock_pool nsp, nsock_event nse, void *ud){ + enum nse_status status = nse_status(nse); + enum nse_type type = nse_type(nse); + + struct nsockbuf_priv *nsbp = (struct nsockbuf_priv *)ud; + + assert(nsbp); + assert(nsbp->callback); + assert(nsbp->bytes_requested || nsbp->lines_requested); + + + if(status == NSE_STATUS_SUCCESS && + (type == NSE_TYPE_READ || + type == NSE_TYPE_TIMER) ) + { + if(type == NSE_TYPE_READ){ + int rec_len; + char *rec = nse_readbuf(nse, &rec_len); + // copy data to buffer. TODO: optimize this!!!! + if(rec_len>0){ + if(!nsbp->buf){ + nsbp->buf = (char*)safe_malloc(rec_len); + nsbp->buf_allocated = rec_len; + }else if(nsbp->buf_allocated - (nsbp->buf_len+rec_len) < 0){// not enough space + nsbp->buf = (char*)safe_realloc(nsbp->buf, nsbp->buf_len + rec_len); + nsbp->buf_allocated = nsbp->buf_len + rec_len; + } + memcpy(&nsbp->buf[nsbp->buf_len], rec, rec_len); + nsbp->buf_len += rec_len; + } + assert(nsbp->buf_allocated >= nsbp->buf_len); + // data okay? do callback + if((nsbp->bytes_requested && + nsbp->buf && nsbp->buf_len>=nsbp->bytes_requested) || + (nsbp->lines_requested && + nsbp->buf_len && memchr(nsbp->buf, '\n', nsbp->buf_len))) + nsbp->callback(nsp, nse, nsbp->userdata); + else //data not okay? request more + nsock_read(nsbp->nsp, nsbp->nsi, nsockbuf_handler, nsbp->timeout_msecs, nsbp); + return; + }else{ + // Ugly hack :) Pretend this was READ event, not TIMER + nsockbuf_event_change_type(nse, NSE_TYPE_READ, nsbp->nsi); + nsbp->fake_read = 1; + nsbp->callback(nsp, nse, nsbp->userdata); + nsbp->fake_read = 0; + return; + } + }else + nsbp->callback(nsp, nse, nsbp->userdata); +} + + + +// Is internal buffer of 'nsockbuf' empty? +bool nsockbuf_empty(nsock_iod nsi){ + struct nsockbuf_priv *nsbp = NULL; + if(nsi) + nsbp = nsockbuf_map[nsi]; + if(!nsbp) + return(true); + if(nsbp->buf_len) + return(false); + return(true); +} + + +/*****************/ +/* Other nsock's hacks: */ + +// How many microseconds went from setting this event till receiving it? +long nsockbuf_geteventtime(nsock_event vnse){ + msevent *nse = (msevent *)vnse; + // do this in microseconds + long d = TIMEVAL_SUBTRACT(*nsock_gettimeofday(), nse->time_created); + return(d); + +} + +// Brutally change nsocks_event type and nsi. +// This is used to force change of TIMER to READ event. +void nsockbuf_event_change_type(nsock_event nse, enum nse_type type, nsock_iod nsi){ + struct msevent *mse = (struct msevent*)nse; + mse->type = type; + mse->iod = (msiod*) nsi; +} + + +/****************************************************************************/ + + + +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)); +} + + +/* Can be executed only in state GSS_WAITING_FOR_INPUT. + Ask for new port. First it sets target_*, than it runs handler */ +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; + if(reason_txt) + free(reason_txt); + reason_txt = NULL; + + handler(GSEE_NOEVENT, NULL); +} + +/* Can be executed only in state GSS_WAITING_FOR_OUTPUT. + Return state of last scanned port. It runs handler before. */ +struct gse_gps_tmp GSEProxy_Interface::getportstatus(){ + static struct gse_gps_tmp ggt; + if(ggt.reason_txt) + free(ggt.reason_txt); + + ggt.host = target_host; + ggt.port = target_port; + ggt.protocol = target_proto; + ggt.reason = reason; + if(!reason_txt) + set_reason_txt(""); + ggt.reason_txt= strdup(reason_txt); + + handler(GSEE_NOEVENT, NULL); + return(ggt); +} + +/* Constructor :) It's not normal because constructors couldn't be inherited. */ +void GSEProxy_Interface::GSEProxy_Int_constr(GSEConnection *ngsc, CHAIN *nchain){ + assert(ngsc && nchain); + + gsc = ngsc; + reason = GSER_NONE; + reason_txt = NULL; + target_host = NULL; + target_port = 0; + target_proto = 0; + + reuse_counter = 0; + + chain = nchain; + assert(chain); + + state = GSS_INTERNAL_STATE_0; + handler(GSEE_NOEVENT, NULL); +} + +/* Set error message. This can be also information why port was classified + * as such 'reason'. For example if you classified port as 'FILTERED', you can set this + * information to 'timeout received'. But no one says this message will be used :) */ +void GSEProxy_Interface::set_reason_txt(const char *fmt, ...){ + va_list ap; + char buf[512]; + char buf2[512]; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf),fmt,ap); + va_end(ap); + + char target_str[256]; + strncpy(target_str, HOST_tostr(target_host), sizeof(target_str)); + + snprintf(buf2, sizeof(buf2),"(%s->%s:%i) %s", + chain->engine!=GSEENG_NSOCK?CHAIN_tostr(chain):"localhost", target_str,target_port, buf); + if(reason_txt) + free(reason_txt); + reason_txt = strdup(buf2); + + return; +} + + +GSEProxy_Interface::~GSEProxy_Interface(){ + if(reason_txt) + free(reason_txt); + if(target_host) + HOST_delete(target_host); +} + +/* Get static string with target name and port number. */ +char *GSEProxy_Interface::target_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); +} + +/* Select whether we should use PROXY or TARGET'S timer. + * If you select PROXY you will receive quite big 'o.maxRttTimeout()'. + * It's possible to receive true proxy timeout (GSE_PROXY_TRUE) but don't rely + * on that! I don't want to break trasmission because timeout was too small! + * You should know, that value of last timeout is recorded in 'last_gto'. + * When READ event occurs that value is used to identify object which will + * have updated timer. For example after you select GSE_TARGET, during next 'READ' + * event your target_host->to will be updated. And after GSE_PROXY your chain->timeout will be updated. */ +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; + if(o.debugging > GSEDEBUGGING+2) + printf("PROXY_get_timeout: TARGET*4 %i\n", (target_host->ths->to.timeout / 1000) *4); + + // Normal timeout is a bit too aggresive for me. + // We multilpy it *4 if timeout is counted. If it's just after initialization, + // we use initialized value. + int mul = 4; + if(target_host->ths->to.srtt == -1) + mul = 1; + return((target_host->ths->to.timeout / 1000)*mul ); + } + } + // return proxies times :) + assert(chain); + last_gto = GSE_PROXY; + + if(gto == GSE_PROXY_TRUE){ + if(o.debugging > GSEDEBUGGING+2) + printf("PROXY_get_timeout: CHAIN_FORCE %i\n",chain->to.timeout / 1000); + return(chain->to.timeout / 1000); + } + + + if(o.debugging > GSEDEBUGGING+2) + printf("PROXY_get_timeout: proxy=maxrtt %i\n",o.maxRttTimeout()); + return(o.maxRttTimeout()); // always should be quite big + +} + +/* During handling event get pointer to timeout_info structure that should be updated */ +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); +} + +/* Increase reuse_counter. We use the same connection to scan more than one port. */ +void GSEProxy_Interface::increase_reuse_counter(){ // increase reuse_counter + reuse_counter++; +} + +/* Can this connection be reused one more time? */ +bool GSEProxy_Interface::can_be_reused(){ // can this connection be reused one more time? + if(reuse_counternsp, gsc->nsi, + gse_generic_handler, + get_timeout(GSE_TARGET), + this, /* user data ! */ + HOST_getsa(target_host), + HOST_getsasize(target_host), + target_port); + reason = GSER_NONE; + break; + case GSEE_TIMEOUT: + set_reason_txt("connection timeouted"); + reason = GSER_FILTERED; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_EOF: + set_reason_txt("connection closed"); + 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_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; // HTTP/1.1 XXX + char *http_code_txt; // HTTP/1.1 000 Xxxxx + int http_len; // Content-Lenght field + bool read_eof; + Pcre *re_http_code; + Pcre *re_http_len; + Pcre *re_http_none; + + int http_type; + char *http_format_str; + static bool filtered_message; // already printed message about filtered ports? + + char *get_auth_header(); +public: + void handler(enum gse_events eve,nsock_event nse); + + +public: + GSEProxy_HttpConnect(int nhttp_type); + ~GSEProxy_HttpConnect(); +}; + +bool GSEProxy_HttpConnect::filtered_message; + +GSEProxy_HttpConnect::GSEProxy_HttpConnect(int nhttp_type){ + re_http_code = new Pcre("^HTTP/1\\.[01] ([0-9]{3}) ?([^\n\r]*)[\n\r]*$", 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_code_txt = NULL; + http_format_str = ""; + if(http_type == HTTPCONNECT){ + http_format_str = + "CONNECT %s:%i HTTP/1.0\n" // we don't support chunking yet, so not version 1.1 + "Proxy-Connection: Keep-Alive\n" + "Host: %s:%i\n" + "%s\n"; + }else{ + http_format_str = + "GET http://%s:%i/ HTTP/1.0\n" + "Proxy-Connection: Keep-Alive\n" + "Host: %s:%i\n" + "%s\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; + if(http_code_txt) + free(http_code_txt); +} + +/* Get http authorization header. It's something like 'Proxy-authorization: Basic xxx\n' + If chain->user or chain->pass is void return empty string.*/ +char *GSEProxy_HttpConnect::get_auth_header(){ + if(chain->user && chain->pass){ + char b1[512]; + char b2[512]; + static char bo[512]; + snprintf(b1, sizeof(b1),"%s:%s", chain->user, chain->pass); + b64enc(b2, sizeof(b2), (u8*)b1, strlen(b1)); + snprintf(bo, sizeof(bo), "Proxy-Authorization: Basic %s\n",b2); + return(bo); + } + return(""); +} + + +void GSEProxy_HttpConnect::handler(enum gse_events eve,nsock_event nse){ + char *rec = NULL; + 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: // send query + switch(eve){ + case GSEE_NOEVENT: + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_PROXY), + this, + http_format_str, + HOST_tostr(target_host), target_port, + HOST_tostr(target_host), target_port, + get_auth_header()); + + http_code = 0; + http_len = 0; + read_eof = false; + + nsockbuf_readline(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_TARGET), + 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; + 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, false) >= 0){ // end of header. -> null line + state = GSS_INTERNAL_STATE_2; + handler(GSEE_NOEVENT, nse); + return; + } + if(re_http_code->match(rec, rec_len, false) >= 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",http_code); + if(http_code_txt) + free(http_code_txt); + http_code_txt = strdup(re_http_code->get(rec, 2)); + } + if(re_http_len->match(rec, rec_len, false) >= 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", http_len); + } + // header not ended, continue :) + nsockbuf_readline(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_PROXY), + 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("http code = 400: bad request on gateway"); + reason = GSER_GW_ERROR; + }else if(http_code == 403){ + set_reason_txt("http code = 403: forbidden"); + if(!filtered_message){ + error("At least one port was marked FILTERED because is forbidden by gateway configuration."); + filtered_message = true; + } + reason = GSER_GW_FILTERED; + }else if(http_code == 502 && http_type == HTTPGET){ // bad gateway = open + reason = GSER_OPENED; + }else if(http_code >= 500 && http_code <= 599){ + 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 if(http_code == 407){ + set_reason_txt("Proxy Authentication Required, are you sure you specified valid username and password?"); + reason = GSER_GW_ERROR; + }else if(http_code == 405){ + set_reason_txt("Proxy error: HTTP Method not allowed! You can try using engine '%s'.", + http_type == HTTPGET?"httpconnect":"httpget"); + reason = GSER_GW_ERROR; + }else{ + set_reason_txt("can't understand HTTP code %i '%s'", http_code, http_code_txt); + 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 + if(!can_be_reused()) + state = GSS_DEAD; + else{ + state = GSS_INTERNAL_STATE_3; + increase_reuse_counter(); + } + 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(GSE_PROXY), + 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"); + nsock_read(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_PROXY_TRUE)/4, // 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->ip){ // socks4a, no dns + 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(GSE_PROXY), + this, (char*)&s4r, s4rlen); + + nsock_read(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_TARGET), + 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; + + 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; // allow ports <1025 + static bool printed_warning; + Pcre *re_answer; + int too_many_users; // too many users logged to ftp server, we're waiting while + struct timeout_info to_last; // used in one place to rollback target timer + +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\n]*)", 0); + too_many_users = 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; + if(eve == GSEE_TIMEOUT && too_many_users){ + state = GSS_DEAD; + return; + } + + switch(state){ + case GSS_INTERNAL_STATE_0: // get banner + switch(eve){ + case GSEE_NOEVENT: + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: reading ftp banner"); + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, + get_timeout(GSE_PROXY)+10000, this); // one line, quite big timeout + break; + case GSEE_EOF: + case GSEE_TIMEOUT: + + if(eve == GSEE_EOF) + set_reason_txt("connection closed while waiting for ftp banner"); + else + set_reason_txt("connection timeouted while waiting for ftp banner"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); + if(rec_len && re_answer->match(rec, rec_len, false) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: ftp banner received, ftp=%i txt='%s'",ftp_code, re_answer->get(rec, 2)); + if(ftp_code == 421){ // too many users logged + error("Warning: Ftp server says that there are too many connections to server. I'm waiting three seconds."); + too_many_users = 1; + nsock_timer_create(gsc->nsp, gse_generic_handler, 3000, this); + return; + } + if(ftp_code<200 || ftp_code>299){ + set_reason_txt("error while reading ftp 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), 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>2 || o.debugging>1) && 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); + } + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: sending username '%s'", chain->user); + 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 while waiting for answer after sending ftp username"); + else + set_reason_txt("connection timeouted while waiting for answer after sending ftp username"); + 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, false) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: ftp username sent, ftp=%i txt='%s'",ftp_code, re_answer->get(rec, 2)); + if(ftp_code>=500 && ftp_code<=599){ + set_reason_txt("bad ftp 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: + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: sending ftp password"); + 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 while waiting for answer after sending ftp passoword"); + else + set_reason_txt("connection timeouted while waiting for answer after sending ftp passoword"); + 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, false) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: ftp password sent, ftp=%i txt='%s'",ftp_code, re_answer->get(rec, 2)); +/* if(ftp_code == 530){ // too many users logged + error("Warning: Ftp server says that there are too many connections to server. I'm waiting three seconds."); + too_many_users = 1; + nsock_timer_create(gsc->nsp, gse_generic_handler, 3000, this); + return; + }*/ + if(ftp_code>=500 && ftp_code<=599){ + set_reason_txt("bad ftp 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: + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: sending PORT command, ftp=%s", HOST_tostr(target_host)); + portno = htons(target_port); + p1 = ((u8 *) &portno)[0]; + p2 = ((u8 *) &portno)[1]; + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_PROXY), 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(GSE_PROXY), this); // one line + break; + case GSEE_EOF: + case GSEE_TIMEOUT: + if(eve == GSEE_EOF) + set_reason_txt("connection closed while waiting for answer for PORT command"); + else + set_reason_txt("connection timeouted while waiting for answer for PORT command. Please use less aggresive '-T' option or try '--proxy-reuse=0'"); + 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, false) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: PORT command sent, ftp=%i txt='%s'",ftp_code, re_answer->get(rec, 2)); + 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 :) + if(ftp_code == 226){ + error("Warning: at least one port was marked 'filtered' instead of 'open'. " + "Please use less aggresive '-T' option or use '--min-rtt-timeout' with value %lims or more. " + "Also you can try option '--proxy-reuse=0'.", (long)(get_timeout(GSE_TARGET)*1.3) ); + state = GSS_WAITING_FOR_OUTPUT; + reason = GSER_GW_FILTERED; + return; + }else if(ftp_code != 200) + error("Warning: unknown response %i for ftp PORT command, it should be 200. ignoring",ftp_code); + state = GSS_INTERNAL_STATE_4; + 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_4: // send LIST command + switch(eve){ + case GSEE_NOEVENT: + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: sending LIST command, and waiting %lims", get_timeout(GSE_TARGET)); + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_PROXY), this, + "LIST\r\n"); + // save target's timer + assert(target_host && target_host->ths); + to_last = target_host->ths->to; + + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_TARGET), this); // one line + break; + case GSEE_EOF: + set_reason_txt("connection closed after LIST command"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_TIMEOUT: + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: LIST command timeouted"); + nsockbuf_free(gsc->nsi); + 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, false) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: LIST command sent, ftp=%i txt='%s'",ftp_code, re_answer->get(rec, 2)); + if(ftp_code == 150){ // wait for next message. + // Timeout is BAD counted here, restore previous + target_host->ths->to = to_last; + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_TARGET), this); // one line + break; + } + if(ftp_code == 425 || ftp_code == 426){ + reason = GSER_CLOSED; + state = GSS_WAITING_FOR_OUTPUT; + break; + } + // 550 is on bad configured 'VxWorks (5.4)' where server founds out that + // it can't acces directory. But we're sure the port is open. + if(ftp_code == 226 || ftp_code == 550){ + reason = GSER_OPENED; + state = GSS_WAITING_FOR_OUTPUT; + break; + } + set_reason_txt("bad ftp response for LIST command: %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(GSE_TARGET), this); // one line + break; + default: + assert(0); + } + break; + case GSS_WAITING_FOR_OUTPUT: + if(reason == GSER_GW_ERROR || reason == GSER_GW_FILTERED || !can_be_reused() ){ + state = GSS_DEAD; + }else if(GSER_FILTERED){ + state = GSS_INTERNAL_STATE_5; + handler(GSEE_NOEVENT, nse); + return; + }else{ + state = GSS_WAITING_FOR_INPUT; + increase_reuse_counter(); + } + break; + case GSS_INTERNAL_STATE_5: // ABOR after LIST timeouted + switch(eve){ + case GSEE_NOEVENT: + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: sending ABOR after LIST timeouted"); + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_PROXY), this, + "ABOR\r\n"); + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_TARGET), this); // one line + break; + case GSEE_EOF: + case GSEE_TIMEOUT: + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: LIST command timeouted/eof"); + set_reason_txt("connection not suitable for reuse"); + state = GSS_DEAD; + break; + case GSEE_READ: + rec = nsockbuf_readbuf(nse, &rec_len); + if(re_answer->match(rec, rec_len, false) >= 0){ + ftp_code = atoi(re_answer->get(rec, 1)); + if(o.debugging > GSEDEBUGGING) + error("FTPBOUNCE: ABOR command sent, ftp=%i txt='%s'", + ftp_code, re_answer->get(rec, 2)); + if(ftp_code == 426){ // one more line + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_TARGET), this); + return; + } + if((ftp_code >=200 && ftp_code<=299) || ftp_code == 500){ // ready to reuse! + increase_reuse_counter(); + state = GSS_WAITING_FOR_INPUT; + break; + } + if(o.debugging || o.verbose) + error("bad answer for ABOR command: %i %s", + ftp_code, re_answer->get(rec, 2)); + set_reason_txt("bad answer for ABOR command: %i %s", + ftp_code, re_answer->get(rec, 2)); + state = GSS_DEAD; + return; + } + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_TARGET), this); // one line + break; + default: + assert(0); + } + 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{ + int use_ssl; + Pcre *re_answer; + static int imap_filtered; // number of filtered ports + +public: + void handler(enum gse_events event,nsock_event nse); + + GSEProxy_Imap(int nuse_ssl); + ~GSEProxy_Imap(); +}; + +int GSEProxy_Imap::imap_filtered=0; + +void *GSEProxy_Imap_new(){ + return(new GSEProxy_Imap(0)); +} + +void *GSEProxy_ImapS_new(){ + return(new GSEProxy_Imap(1)); +} + + +GSEProxy_Imap::GSEProxy_Imap(int nuse_ssl){ + re_answer = new Pcre("^[0-9]+ ([^ ]+) ([^\r\n]*)", 0); + use_ssl = nuse_ssl; +} + + +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: + if(use_ssl){ + if(o.debugging > GSEDEBUGGING) + error("IMAPS: switching to ssl"); + nsock_reconnect_ssl(gsc->nsp, gsc->nsi, gse_generic_handler, + get_timeout(GSE_PROXY), this, NULL); + return; + } + case GSEE_CONNECT: + if(o.debugging > GSEDEBUGGING) + error("IMAP: sending LOGIN command"); + 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 after LOGIN command"); + else + set_reason_txt("connection timeout after LOGIN command"); + 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, false) >= 0){ + char *res = re_answer->get(rec, 1); + if(o.debugging > GSEDEBUGGING) + error("IMAP: LOGIN command sent: %s %s", res, re_answer->get(rec, 2)); + if(strcmp(res, "OK")!=0){ + set_reason_txt("bad imap username/password: %s %s", res, re_answer->get(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: + if(o.debugging > GSEDEBUGGING) + error("IMAP: sending SELECT command"); + nsock_printf(gsc->nsp, gsc->nsi, + gse_generic_handler, get_timeout(GSE_PROXY), this, + "1 SELECT \"{%s:%i}\"\r\n", + HOST_tostr(target_host), + target_port); + nsockbuf_readline(gsc->nsp, gsc->nsi, gse_generic_handler, get_timeout(GSE_TARGET), this); // one line + break; + case GSEE_EOF: + set_reason_txt("connection closed after SELECT command"); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + break; + case GSEE_TIMEOUT: + imap_filtered++; + if(imap_filtered > 38){ + error("Warning: Please be carefull with scanning big amount of 'filtered' ports using imap engine. " + "With normal imapd installation nmap will die after scanning about 40 filtered ports. " + "Remember that scanning filtered port opens imapd process on zombie machine for a while. " + "If you really want to scan filtered ports, you can think of setting --min-rtt-timeout to bigger" + "value than timeouts on zombie."); + } + set_reason_txt("connection timeouted after SELECT command"); + 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, false) >= 0){ + char *res = re_answer->get(rec, 1); + char *ans = re_answer->get(rec, 2); + if(o.debugging > GSEDEBUGGING) + error("IMAP: SELECT command sent: %s %s", res, ans); + if(strcmp(res,"NO")==0){ + if(strstr(ans,"refused")){ + reason = GSER_CLOSED; + state = GSS_WAITING_FOR_OUTPUT; + return; + }else if(strstr(ans,"Mailbox doesn't exist")){ + set_reason_txt("Sorry, using this imap server you can't do port scanning."); + reason = GSER_GW_ERROR; + state = GSS_WAITING_FOR_OUTPUT; + return; + }else{ + // other binary(?) data + 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(GSE_TARGET), 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 nmap-4.20ALPHA4-orig/gse.h nmap-4.20ALPHA4-gsebeta2/gse.h --- nmap-4.20ALPHA4-orig/gse.h 1970-01-01 01:00:00.000000000 +0100 +++ nmap-4.20ALPHA4-gsebeta2/gse.h 2006-08-12 09:32:26.000000000 +0200 @@ -0,0 +1,428 @@ + +/*************************************************************************** + * gse.h -- General Scanning Engine, asynchronous proxy chaining * + * * + ***********************IMPORTANT NMAP LICENSE TERMS************************ + * * + * The Nmap Security Scanner is (C) 1996-2004 Insecure.Com LLC. Nmap * + * is also a registered trademark of Insecure.Com LLC. This program is * + * free software; you may redistribute and/or modify it under the * + * terms of the GNU General Public License as published by the Free * + * Software Foundation; Version 2. This guarantees your right to use, * + * modify, and redistribute this software under certain conditions. If * + * you wish to embed Nmap technology into proprietary software, we may be * + * willing to sell alternative licenses (contact sales@insecure.com). * + * Many security scanner vendors already license Nmap technology such as * + * our remote OS fingerprinting database and code, service/version * + * detection system, and port scanning code. * + * * + * Note that the GPL places important restrictions on "derived works", yet * + * it does not provide a detailed definition of that term. To avoid * + * misunderstandings, we consider an application to constitute a * + * "derivative work" for the purpose of this license if it does any of the * + * following: * + * o Integrates source code from Nmap * + * o Reads or includes Nmap copyrighted data files, such as * + * nmap-os-fingerprints or nmap-service-probes. * + * o Executes Nmap and parses the results (as opposed to typical shell or * + * execution-menu apps, which simply display raw Nmap output and so are * + * not derivative works.) * + * o Integrates/includes/aggregates Nmap into a proprietary executable * + * installer, such as those produced by InstallShield. * + * o Links to a library or executes a program that does any of the above * + * * + * The term "Nmap" should be taken to also include any portions or derived * + * works of Nmap. This list is not exclusive, but is just meant to * + * clarify our interpretation of derived works with some common examples. * + * These restrictions only apply when you actually redistribute Nmap. For * + * example, nothing stops you from writing and selling a proprietary * + * front-end to Nmap. Just distribute it by itself, and point people to * + * http://www.insecure.org/nmap/ to download Nmap. * + * * + * We don't consider these to be added restrictions on top of the GPL, but * + * just a clarification of how we interpret "derived works" as it applies * + * to our GPL-licensed Nmap product. This is similar to the way Linus * + * Torvalds has announced his interpretation of how "derived works" * + * applies to Linux kernel modules. Our interpretation refers only to * + * Nmap - we don't speak for any other GPL products. * + * * + * If you have any questions about the GPL licensing restrictions on using * + * Nmap in non-GPL works, we would be happy to help. As mentioned above, * + * we also offer alternative license to integrate Nmap into proprietary * + * applications and appliances. These contracts have been sold to many * + * security vendors, and generally include a perpetual license as well as * + * providing for priority support and updates as well as helping to fund * + * the continued development of Nmap technology. Please email * + * sales@insecure.com for further information. * + * * + * As a special exception to the GPL terms, Insecure.Com LLC grants * + * permission to link the code of this program with any version of the * + * OpenSSL library which is distributed under a license identical to that * + * listed in the included Copying.OpenSSL file, and distribute linked * + * combinations including the two. You must obey the GNU GPL in all * + * respects for all of the code used other than OpenSSL. If you modify * + * this file, you may extend this exception to your version of the file, * + * but you are not obligated to do so. * + * * + * If you received these files with a written license agreement or * + * contract stating terms other than the terms above, then that * + * alternative license agreement takes precedence over these comments. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes (none * + * have been found so far). * + * * + * Source code also allows you to port Nmap to new platforms, fix bugs, * + * and add new features. You are highly encouraged to send your changes * + * to fyodor@insecure.org for possible incorporation into the main * + * distribution. By sending these changes to Fyodor or one the * + * Insecure.Org development mailing lists, it is assumed that you are * + * offering Fyodor and Insecure.Com LLC the unlimited, non-exclusive right * + * to reuse, modify, and relicense the code. Nmap will always be * + * available Open Source, but this is important because the inability to * + * relicense code has caused devastating problems for other Free Software * + * projects (such as KDE and NASM). We also occasionally relicense the * + * code to third parties as discussed above. If you wish to specify * + * special license conditions of your contributions, just say so when you * + * send them. * + * * + * This program is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details at * + * http://www.gnu.org/copyleft/gpl.html , or in the COPYING file included * + * with Nmap. * + * * + ***************************************************************************/ + +#ifndef GSE_H_ +#define GSE_H_ + +#ifdef HAVE_PCRE_PCRE_H +# include +#else +# include +#endif +#include + +/*************************************************************************** + ENGINES for gse + ***************************************************************************/ +/* Available scanning engines. Order is very important. + When you're modifying this also remember to update + 'struct gsee_definitions' in gse.cc */ +enum gse_engines{ + GSEENG_UNINITIALISED = 0, + GSEENG_NSOCK = 1, + GSEENG_HTTPCONNECT = 2, + GSEENG_HTTPGET = 3, + GSEENG_SOCKS4A = 4, + GSEENG_FTPBOUNCE = 5, + GSEENG_IMAP = 6, + GSEENG_IMAPS = 7, +}; + +/* Flags that can be set for scanning engines. You should + use binary or to set multiple flags. + See second field in 'struct gsee_definitions'*/ +enum gse_flags{ + GSEF_NONE = 0x00, + GSEF_DNS = 0x01, // dns can be resolved on remote side + GSEF_UDP = 0x02, // this engine can handle udp + GSEF_IPV6 = 0x04, // it handles ipv6 protocol + GSEF_FIRST= 0x08, // must only be used as first in chain + GSEF_LAST = 0x10, // must only be used as last in chain + // should be set on engines which are not able to create + // binary tunnel to target +}; + + +/* Type definition for 'struct gsee_definitions' */ +struct gse_eng_definitions{ + enum gse_engines engine; // Engine id. + int flags; // flags set on that engine + u16 default_port; // default port number + void *(*create_fun)(); // Function that will return new object of that engine. + char *names[16]; // Maximum 16 aliases for engine. First name is default. +}; + +/* All GSE engines are defined in this structure. + You can use this data for example in this way: gsee_definitions[GSEENG_FTPBOUNCE].flags */ +extern struct gse_eng_definitions gsee_definitions[]; + +/* How many engines are availble in gsee_definitions? */ +extern int gsee_definitions_len; + + +/*************************************************************************** + HOST -- the storage for host addresses + ***************************************************************************/ +/* The main idea of this structure is to create another abstraction layer + * for both target and proxy hosts. + * The HOST structure can store hostname and ip/ipv6 address. + * This structrue was also intended to be really simple. + * + * You should try not to access internal fields from your code. + */ +struct GSE_Host{ + u32 magic; + int counter; // how many times this structure is used? 0= you can destroy it + 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. (malloced) + char *hostname; // not resolved hostname, what user typed. (malloced) + struct sockaddr *sa; // cached 'sockaddr' structure, used by HOST_getsa() (malloced) + class Target *ths; // address of Target object that is linked to this HOST structure +}; +typedef struct GSE_Host HOST; + + +// Constructors for HOST structure +HOST *HOST_new(char *hostname); // create by hostname +HOST *HOST_new(char *hostname, class Target *nths); // create and link to Target +HOST *HOST_new_sockaddr(struct sockaddr *ss, size_t sslen);// create by ip addr + +// 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. Dns query will be performed if nessesary +void HOST_resolve(HOST *host); +void HOST_resolve(HOST *host, bool printwarning); + +/* Get static string that contains ip of host if possible, + or hostname if no resolved ip is present. + If resolved ip is present in ipv6 format, the result is + ipv6 address captured in square brackets. */ +char *HOST_tostr(HOST *host); + +// Is resolved ip in ipv6 ? +bool HOST_isipv6(HOST *host); + +// Get Target class that is linked to this HOST +class Target *HOST_get_ths(HOST *host); + + +/*************************************************************************** + CHIAN - Contains information needed to create proxy chain. + ***************************************************************************/ +/* This sturcture is basically parsed version of parameter '-sB '. + * Each proxy in chain has one corresponding GSE_Chain structure. + * All Proxies are stored in array '(CHAIN*)o.gsechain'. + * Which contains 'o.gsechain_len' elements. + * (o.gsechain is type 'void*' to ommit including this header in all nmap files) + * + * Every proxy has + * engine type + * proxy_host:proxy_port as address + * user:pass if user selected username/password + * and timeout values. + * Normally timeout_info is not used. In communication we use maximum allowed + * timeouts (o.maxRttTimeout). + * But there can be some cases when we need to know exact RTT of this proxy. + */ + +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. + * The input must be in format: + * "engine:[//][user:pass@]host[:port]" + * many proxies must be separated by space. + * You can't use '@' or ':' in user, pass or host field. + * Please use '\xAB' hex notation if you need to specify those characters. + */ +CHAIN *CHAIN_create(int *chain_len, char *text); + +/* CHAIN_create is runned before o.maxRttTimeout is set. + * Call this function to initialize timeouts of all Proxies in chain + * after o.maxRttTimeout will be set. */ +void CHAIN_initialize_timeouts(CHAIN *chain, int chain_len); + +/* Get static string that describes this chain. */ +char *CHAIN_getallstr(CHAIN *chain, int chain_len); + +// Delete CHAIN array. +void CHAIN_delete(CHAIN *chain, int chain_len); + +// Is it possible to create tunnel through this chain? +bool CHAIN_can_create_tunnel(CHAIN *chain, int chain_len); + +// Is it possible to create UDP scans using this chain? +bool CHAIN_can_do_udp(CHAIN *chain, int chain_len); + +// Get static string to one CHAIN structure. +// Output string will always be in format 'engine://[user:pass]@host:port' +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_INTERNAL_STATE_5, + 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. program should be stopped with error + 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 responsible for building chain. +** We can also say that it's binded to exacly one socket. +** +** Just after creation of object 'proxy_current' will be set to +** first 'proxy' in chain (which must be engine 'GSE_Proxy_NSock') +** +** Then it executes asynchronous events from nsock library, and +** executes 'proxy_current->handler()'. +** If 'proxy_current->state' is 'GSS_ALIVE' connection to next hop +** is created. proxy_current is deleted and replaced by GSE_Proxy_xxx +** corresponding to next engine in chain. +** That sequence is repeated till the last 'proxy' in chain. +** +** You can notice that all proxies doesn't know if they are last in +** chain. They just try to connect to target. If connection fails +** and target is next hop program fails, because it's impossible +** to establish connection through chain. +** +** This class will run some methods from GSEScanner_Interface +** when it'll need to send/receive information about ports on target. +** +** The main function in this class is 'update_proxy_pool', it's runned by +** asynchronous nsock handler. +*/ +class GSEConnection{ +private: + class GSEProxy_Interface *proxy_current; // current GSEProxy_Interface + CHAIN *chain; // chain array. used in building hops. + int chain_len; // how many hops do we have? + int chain_pos; // current position in chain. (this is linked to proxy_current atribute) + + class GSEScanner_Interface *gss; // our parent, we'll need to send/receive some infomation about target ports +public: // GSEProxy_xxx must have access to these fields: + nsock_pool nsp; + nsock_iod nsi; + +private: + bool chain_built(); // has building of this chain ended? (are we currently at the last hop) + void build_hop(); // move chain_pos to next proxy and replace proxy_current with next hop +public: + /* This function is runned by asynchronous nsock handler. + * 'state' in current proxy could be changed (after async event). + * It can be requesting ports to scan, it can inform us that + * connection has been established, etc. Handle this. + * Return value true mens that this object is to be deleted. */ + bool update_proxy_pool(); + +private: + // this functions are helpers for 'update_proxy_pool' + bool conn_write_input(); + void conn_read_output(); + bool conn_alive(); + +public: + /* Constructor. You must specify CHAIN, nsp, nsi, GSEScanner */ + GSEConnection(CHAIN *nchain, int nchain_len, nsock_pool nnsp, nsock_iod nnsi, + class GSEScanner_Interface *ngss); + ~GSEConnection(); +}; + +/*************************************************************************** + GSESCANNER + ***************************************************************************/ +/* This class is used to bind your code with GSC. + There are only few basice methods you need to provide. + I hope that this simple interface will make GSC code really simple to use. + + This methods are runned by GSEConnection:update_proxy_pool when needed. */ +class GSEScanner_Interface{ +public: + // this functions *MUST* be overloaded by you. + + /* This GSEConnection will soon be deleted, create new GSEConnection + if you need. (do *not* try delete this gsc!) */ + virtual void handle_remove(class GSEConnection *gsc)=0; + + /* We need information about port to scan. + If there are no more ports to scan, return false. */ + virtual bool handle_get_new_target(HOST **target, u16 *port, u8 *proto)=0; + + /* 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 to target is now created :) Close it if you don't need it. + (of course information that this port is opened was previously sent using 'handle_result') */ + virtual void handle_alive(nsock_iod nsi)=0; + + // We must have virtual destructor. + virtual ~GSEScanner_Interface(){}; +}; + + + +#endif /*GSE_H_*/ diff -Nraupb nmap-4.20ALPHA4-orig/Makefile.in nmap-4.20ALPHA4-gsebeta2/Makefile.in --- nmap-4.20ALPHA4-orig/Makefile.in 2006-07-04 09:47:07.000000000 +0200 +++ nmap-4.20ALPHA4-gsebeta2/Makefile.in 2006-08-12 09:32:26.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 nmap-4.20ALPHA4-orig/nmap.cc nmap-4.20ALPHA4-gsebeta2/nmap.cc --- nmap-4.20ALPHA4-orig/nmap.cc 2006-07-05 01:05:03.000000000 +0200 +++ nmap-4.20ALPHA4-gsebeta2/nmap.cc 2006-08-12 09:32:26.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" + " -sB : Scan through proxy chain\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; @@ -533,6 +486,7 @@ int nmap_main(int argc, char *argv[]) { {"iL", required_argument, 0, 0}, {"iR", required_argument, 0, 0}, {"sI", required_argument, 0, 0}, + {"sB", required_argument, 0, 0}, {"source_port", required_argument, 0, 'g'}, {"source-port", required_argument, 0, 'g'}, {"randomize_hosts", no_argument, 0, 0}, @@ -582,6 +536,8 @@ int nmap_main(int argc, char *argv[]) { {"log-errors", no_argument, 0, 0}, {"dns_servers", required_argument, 0, 0}, {"dns-servers", required_argument, 0, 0}, + {"proxy_reuse", required_argument, 0, 0}, + {"proxy-reuse", required_argument, 0, 0}, {0, 0, 0, 0} }; @@ -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,"6AD:d::e:Ffg:hIi:M:m:nO::o:P:p:qRrS:s:T:Vv", long_options, &option_index)) != EOF) { switch(arg) { case 0: if (optcmp(long_options[option_index].name, "max-rtt-timeout") == 0) { @@ -780,6 +736,11 @@ int nmap_main(int argc, char *argv[]) { } else if (strcmp(long_options[option_index].name, "sI") == 0) { o.idlescan = 1; idleProxy = optarg; + } else if (strcmp(long_options[option_index].name, "sB") == 0) { + o.gsescan = 1; + o.gse_chain = CHAIN_create(&o.gse_chain_len, optarg); + } else if (strcmp(long_options[option_index].name, "proxy-reuse") == 0) { + o.gse_reuse = atoi(optarg); } else if (strcmp(long_options[option_index].name, "vv") == 0) { /* Compatability hack ... ugly */ o.verbose += 2; @@ -807,12 +768,6 @@ int nmap_main(int argc, char *argv[]) { if (o.isr00t) 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"); - } - break; case 'D': p = optarg; do { @@ -996,8 +951,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; @@ -1013,6 +966,8 @@ int nmap_main(int argc, char *argv[]) { o.udpscan++; break; case 'X': o.xmasscan++;break; + case 'B': assert(0); break; + case 'I': assert(0); break; default: error("Scantype %c not supported\n",*p); printusage(argv[0], -1); break; } p++; @@ -1099,6 +1054,12 @@ int nmap_main(int argc, char *argv[]) { else o.reference_FPs = parse_fingerprint_reference_file("nmap-os-db"); + if(o.gsescan){ + CHAIN_initialize_timeouts((CHAIN*)o.gse_chain, o.gse_chain_len); + if(o.verbose) + error("You selected chain: %s\n",CHAIN_getallstr((CHAIN*)o.gse_chain, o.gse_chain_len)); + } + o.ValidateOptions(); /* Open the log files, now that we know whether the user wants them appended @@ -1258,21 +1219,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 +1490,9 @@ int nmap_main(int argc, char *argv[]) { if (o.ipprotscan) ultra_scan(Targets, ports, IPPROT_SCAN); + if (o.gsescan) + gse_scan(Targets, ports, (CHAIN*)o.gse_chain, o.gse_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 +1502,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) { @@ -1658,6 +1600,8 @@ void nmap_free_mem() { o.reference_FPs = NULL; } AllProbes::service_scan_free(); + if(o.gse_chain) + CHAIN_delete((CHAIN *)o.gse_chain, o.gse_chain_len); } @@ -2090,9 +2034,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 +2057,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 nmap-4.20ALPHA4-orig/nmap.h nmap-4.20ALPHA4-gsebeta2/nmap.h --- nmap-4.20ALPHA4-orig/nmap.h 2006-07-05 01:05:03.000000000 +0200 +++ nmap-4.20ALPHA4-gsebeta2/nmap.h 2006-08-12 09:32:26.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 nmap-4.20ALPHA4-orig/NmapOps.cc nmap-4.20ALPHA4-gsebeta2/NmapOps.cc --- nmap-4.20ALPHA4-orig/NmapOps.cc 2006-07-04 10:00:24.000000000 +0200 +++ nmap-4.20ALPHA4-gsebeta2/NmapOps.cc 2006-08-12 09:32:26.000000000 +0200 @@ -104,6 +104,7 @@ #ifdef WIN32 #include "winfix.h" #endif +#include "gse.h" NmapOps o; @@ -226,7 +227,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 +254,14 @@ void NmapOps::Initialize() { noninteractive = false; current_scantype = STYPE_UNKNOWN; release_memory = false; - + gsescan = 0; + gse_reuse = 4096; + gse_chain = NULL; + gse_chain_len = 0; } 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 +396,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