diff --git a/CHANGELOG b/CHANGELOG index f5edc88c9..fe19bce1b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,18 @@ # Nmap Changelog ($Id$); -*-text-*- Nmap 4.02ALPHA1 +o Added the --log-errors option, which causes most warnings and error + messages that are printed to interactive-mode output (stdout/stderr) + to also be printed to the normal-format output file (if you + specified one). This will not work for most errors related to bad + command-line arguments, as Nmap may not have initialized its output + files yet. In addition, some Nmap error/warning messages use a + different system that does not yet support this option. + +o Rewrote much of the Nmap results output functions to be more + efficient and support --log-errors. I hope I didn't break + anything. + o Fixed a flaw in the scan engine which could (in rare cases) lead to a deadlock situation that prevents a scan from completing. Thanks to Ganga Bhavani (GBhavani(a)everdreamcorp.com) for reporting diff --git a/NmapOps.cc b/NmapOps.cc index 0ab43fdfd..05bcd3d9b 100644 --- a/NmapOps.cc +++ b/NmapOps.cc @@ -228,7 +228,7 @@ void NmapOps::Initialize() { rpcscan = nullscan = xmasscan = fragscan = synscan = windowscan = 0; maimonscan = idlescan = finscan = udpscan = ipprotscan = noresolve = 0; force = append_output = 0; - memset(logfd, 0, sizeof(FILE *) * LOG_TYPES); + memset(logfd, 0, sizeof(FILE *) * LOG_NUM_FILES); ttl = -1; badsum = 0; nmap_stdout = stdout; @@ -245,6 +245,7 @@ void NmapOps::Initialize() { xsl_stylesheet = strdup(tmpxsl); spoof_mac_set = false; mass_dns = true; + log_errors = false; resolve_all = 0; dns_servers = NULL; noninteractive = false; diff --git a/NmapOps.h b/NmapOps.h index d7e1d21cb..835b421a8 100644 --- a/NmapOps.h +++ b/NmapOps.h @@ -278,7 +278,7 @@ class NmapOps { int noresolve; int force; /* force nmap to continue on even when the outcome seems somewhat certain */ int append_output; /* Append to any output files rather than overwrite */ - FILE *logfd[LOG_TYPES]; + FILE *logfd[LOG_NUM_FILES]; FILE *nmap_stdout; /* Nmap standard output */ int ttl; // Time to live int badsum; @@ -286,6 +286,7 @@ class NmapOps { bool mass_dns; int resolve_all; char *dns_servers; + bool log_errors; // Statistics Options set in nmap.cc int numhosts_scanned; diff --git a/docs/nmap.1 b/docs/nmap.1 index 6cb716443..8e5093ee4 100644 --- a/docs/nmap.1 +++ b/docs/nmap.1 @@ -2,7 +2,7 @@ .\" It was generated using the DocBook XSL Stylesheets (version 1.69.1). .\" Instead of manually editing it, you probably should edit the DocBook XML .\" source for it and then use the DocBook XSL Stylesheets to regenerate it. -.TH "NMAP" "1" "03/02/2006" "" "Nmap Reference Guide" +.TH "NMAP" "1" "03/03/2006" "" "Nmap Reference Guide" .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) @@ -1138,6 +1138,9 @@ instead. .TP \fB\-\-iflist\fR (List interfaces and routes) Prints the interface list and system routes as detected by Nmap. This is useful for debugging routing problems or device mischaracterization (such as Nmap treating a PPP connection as Ethernet). +.TP +\fB\-\-log\-errors\fR (Log errors/warnings to normal mode output file) +Warnings and errors printed by Nmap usually go only to the screen (interactive output), leaving any specified normal\-fomat output files uncluttered. But when you do want to see those messages in the normal output file you specified, add this option. It is useful when you aren't watching the interactive output or are trying to debug a problem. The messages will also still appear in interactive mode. This will not work for most errors related to bad command\-line arguments, as Nmap may not have initialized its output files yet. In addition, some Nmap error/warning messages use a different system that does not yet support this option. An alternative to using this option is redirecting interactive output (including the standard error stream) to a file. While most UNIX shells make that approach easy, it can be difficult on Windows. .PP \fBMiscellaneous output options\fR .TP diff --git a/docs/nmap.usage.txt b/docs/nmap.usage.txt index e59ce15f1..6aa131e9d 100644 --- a/docs/nmap.usage.txt +++ b/docs/nmap.usage.txt @@ -67,6 +67,7 @@ OUTPUT: -d[level]: Set or increase debugging level (Up to 9 is meaningful) --packet-trace: Show all packets sent and received --iflist: Print host interfaces and routes (for debugging) + --log-errors: Log errors/warnings to the normal-format output file --append-output: Append to rather than clobber specified output files --resume : Resume an aborted scan --stylesheet : XSL stylesheet to transform XML output to HTML diff --git a/nmap.cc b/nmap.cc index d2912ed95..b38cb3e47 100644 --- a/nmap.cc +++ b/nmap.cc @@ -331,6 +331,8 @@ int nmap_main(int argc, char *argv[]) { {"version-all", no_argument, 0, 0}, {"system_dns", no_argument, 0, 0}, {"system-dns", no_argument, 0, 0}, + {"log_errors", no_argument, 0, 0}, + {"log-errors", no_argument, 0, 0}, {"dns_servers", required_argument, 0, 0}, {"dns-servers", required_argument, 0, 0}, {0, 0, 0, 0} @@ -484,6 +486,8 @@ int nmap_main(int argc, char *argv[]) { o.mass_dns = false; } else if (optcmp(long_options[option_index].name, "dns-servers") == 0) { o.dns_servers = strdup(optarg); + } else if (optcmp(long_options[option_index].name, "log-errors") == 0) { + o.log_errors = 1; } else if (strcmp(long_options[option_index].name, "webxml") == 0) { o.setXSLStyleSheet("http://www.insecure.org/nmap/data/nmap.xsl"); } else if (strcmp(long_options[option_index].name, "oN") == 0) { @@ -1700,6 +1704,7 @@ printf("%s %s ( %s )\n" " -d[level]: Set or increase debugging level (Up to 9 is meaningful)\n" " --packet-trace: Show all packets sent and received\n" " --iflist: Print host interfaces and routes (for debugging)\n" + " --log-errors: Log errors/warnings to the normal-format output file\n" " --append-output: Append to rather than clobber specified output files\n" " --resume : Resume an aborted scan\n" " --stylesheet : XSL stylesheet to transform XML output to HTML\n" diff --git a/nmap_error.cc b/nmap_error.cc index 42fe3ced9..9183d3d14 100644 --- a/nmap_error.cc +++ b/nmap_error.cc @@ -99,6 +99,10 @@ /* $Id$ */ #include "nmap_error.h" +#include "output.h" +#include "NmapOps.h" + +extern NmapOps o; #ifdef WIN32 #include @@ -109,55 +113,102 @@ extern void CloseLibs(void); #endif void fatal(const char *fmt, ...) { -va_list ap; -va_start(ap, fmt); -fflush(stdout); -vfprintf(stderr, fmt, ap); -fprintf(stderr, "\nQUITTING!\n"); -fflush(stderr); -va_end(ap); -exit(1); + va_list ap; + va_start(ap, fmt); + log_vwrite(LOG_STDERR, fmt, ap); + va_end(ap); + if (o.log_errors) { + va_start(ap, fmt); + log_vwrite(LOG_NORMAL, fmt, ap); + va_end(ap); + } + log_write(o.log_errors? LOG_NORMAL|LOG_STDERR : LOG_STDERR, "QUITTING!\n"); + exit(1); } void error(const char *fmt, ...) { -va_list ap; -va_start(ap, fmt); -fflush(stdout); -vfprintf(stderr, fmt, ap); -fprintf(stderr, "\n"); -fflush(stderr); -va_end(ap); -return; + va_list ap; + + va_start(ap, fmt); + log_vwrite(LOG_STDERR, fmt, ap); + va_end(ap); + + if (o.log_errors) { + va_start(ap, fmt); + log_vwrite(LOG_NORMAL, fmt, ap); + va_end(ap); + } + + return; } void pfatal(const char *err, ...) { #ifdef WIN32 - int lasterror =0; - char *errstr = NULL; + int lasterror =0; + char *errstr = NULL; #endif - va_list ap;va_start(ap, err); - fflush(stdout); - vfprintf(stderr, err, ap); - va_end(ap); + va_list ap; + + va_start(ap, err); + log_vwrite(LOG_STDERR, err, ap); + va_end(ap); + + if (o.log_errors) { + va_start(ap, err); + log_vwrite(LOG_NORMAL, err, ap); + va_end(ap); + } + #ifdef WIN32 - lasterror = GetLastError(); - FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, NULL, lasterror, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + lasterror = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + NULL, lasterror, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &errstr, 0, NULL); - fprintf(stderr, ": %s (%d)\n", errstr, lasterror); - HeapFree(GetProcessHeap(), 0, errstr); + log_write(o.log_errors? LOG_NORMAL|LOG_STDERR : LOG_STDERR, ": %s (%d)\n", + errstr, lasterror); + HeapFree(GetProcessHeap(), 0, errstr); #else - perror(" "); + log_write(o.log_errors? LOG_NORMAL|LOG_STDERR : LOG_STDERR, ": %s (%d)\n", + strerror(errno), errno); #endif /* WIN32 perror() compatability switch */ - fflush(stderr); - exit(1); + if (o.log_errors) log_flush(LOG_STDOUT); + fflush(stderr); + exit(1); } +/* This function is the Nmap version of perror. It is just copy and + pasted from pfatal(), except the exit has been replaced with a + return. */ void gh_perror(const char *err, ...) { -va_list ap;va_start(ap, err); -fflush(stdout); -vfprintf(stderr, err, ap); -va_end(ap); -perror(" "); -fflush(stderr); -return; +#ifdef WIN32 + int lasterror =0; + char *errstr = NULL; +#endif + va_list ap; + + va_start(ap, err); + log_vwrite(LOG_STDERR, err, ap); + va_end(ap); + + if (o.log_errors) { + va_start(ap, err); + log_vwrite(LOG_NORMAL, err, ap); + va_end(ap); + } + +#ifdef WIN32 + lasterror = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + NULL, lasterror, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &errstr, 0, NULL); + log_write(o.log_errors? LOG_NORMAL|LOG_STDERR : LOG_STDERR, ": %s (%d)\n", + errstr, lasterror); + HeapFree(GetProcessHeap(), 0, errstr); +#else + log_write(o.log_errors? LOG_NORMAL|LOG_STDERR : LOG_STDERR, ": %s (%d)\n", + strerror(errno), errno); +#endif /* WIN32 perror() compatability switch */ + if (o.log_errors) log_flush(LOG_STDOUT); + fflush(stderr); + return; } diff --git a/nmapfe/nmapfe_error.c b/nmapfe/nmapfe_error.c index 255d23f49..797c0320f 100644 --- a/nmapfe/nmapfe_error.c +++ b/nmapfe/nmapfe_error.c @@ -148,14 +148,3 @@ void pfatal(char *err, ...) { fflush(stderr); exit(1); } - - -void gh_perror(char *err, ...) { - va_list ap;va_start(ap, err); - fflush(stdout); - vfprintf(stderr, err, ap); - va_end(ap); - perror(" "); - fflush(stderr); - return; -} diff --git a/nmapfe/nmapfe_error.h b/nmapfe/nmapfe_error.h index 2a4c0e617..c6f7b7158 100644 --- a/nmapfe/nmapfe_error.h +++ b/nmapfe/nmapfe_error.h @@ -118,6 +118,5 @@ void fatal(char *fmt, ...); void error(char *fmt, ...); void pfatal(char *err, ...); -void gh_perror(char *err, ...); #endif /* NMAPFE_ERROR_H */ diff --git a/output.cc b/output.cc index 2b0415b5d..45b0e6406 100644 --- a/output.cc +++ b/output.cc @@ -116,7 +116,45 @@ namespace std {}; using namespace std; extern NmapOps o; -static char *logtypes[LOG_TYPES]=LOG_NAMES; +static char *logtypes[LOG_NUM_FILES]=LOG_NAMES; + +/* Used in creating skript kiddie style output. |<-R4d! */ +void skid_output(char *s) +{ + int i; + for (i=0;s[i];i++) + if (rand()%2==0) + /* Substitutions commented out are not known to me, but maybe look nice */ + switch(s[i]) + { + case 'A': s[i]='4'; break; + /* case 'B': s[i]='8'; break; + case 'b': s[i]='6'; break; + case 'c': s[i]='k'; break; + case 'C': s[i]='K'; break; */ + case 'e': + case 'E': s[i]='3'; break; + case 'i': + case 'I': s[i]="!|1"[rand()%3]; break; + /* case 'k': s[i]='c'; break; + case 'K': s[i]='C'; break;*/ + case 'o': + case 'O': s[i]='0'; break; + case 's': + case 'S': + if (s[i+1] && !isalnum((int) s[i+1])) + s[i] = 'z'; + else s[i] = '$'; + break; + case 'z': s[i]='s'; break; + case 'Z': s[i]='S'; break; + } + else + { + if (s[i]>='A' && s[i]<='Z' && (rand()%3==0)) s[i]+='a'-'A'; + else if (s[i]>='a' && s[i]<='z' && (rand()%3==0)) s[i]-='a'-'A'; + } +} // Creates an XML element for the information given in @@ -617,53 +655,100 @@ char* xml_convert (const char* str) { return temp; } +/* This is the workhorse of the logging functions. Usually it is + called through log_write(), but it can be called directly if you + are dealing with a vfprintf-style va_list. Unlike log_write, YOU + CAN ONLY CALL THIS WITH ONE LOG TYPE (not a bitmask full of them). + In addition, YOU MUST SANDWHICH EACH EXECUTION IF THIS CALL BETWEEN + va_start() AND va_end() calls. */ +void log_vwrite(int logt, const char *fmt, va_list ap) { + static char *writebuf = NULL;; + int writebuflen = 8192; + bool skid_noxlate = false; + int rc = 0; + int len; + int fileidx = 0; + int l; + + if (!writebuf) + writebuf = (char *) safe_malloc(writebuflen); + + if (logt == LOG_SKID_NOXLT) { + logt = LOG_SKID; + skid_noxlate = true; + } + + switch(logt) { + case LOG_STDOUT: + vfprintf(o.nmap_stdout, fmt, ap); + break; + + case LOG_STDERR: + fflush(stdout); // Otherwise some systems will print stderr out of order + vfprintf(stderr, fmt, ap); + break; + + case LOG_NORMAL: + case LOG_MACHINE: + case LOG_SKID: + case LOG_XML: + l = logt; + fileidx = 0; + while ((l&1)==0) { fileidx++; l>>=1; } + assert(fileidx < LOG_NUM_FILES); + if (o.logfd[fileidx]) { + len = vsnprintf(writebuf, writebuflen, fmt, ap); + if (len == 0) { + return; + } else if (len < 0) { + fprintf(stderr, "vnsprintf returned %d in %s -- bizarre. Quitting.\n", len, __FUNCTION__); + exit(1); + } else if (len >= writebuflen) { + fprintf(stderr, "%s: write buffer not large enough -- need to increase from %d to at least %d (logt == %d). Please email this message to fyodor@insecure.org. Quitting.\n", __FUNCTION__, writebuflen, len, logt); + exit(1); + } + if (logt == LOG_SKID && !skid_noxlate) + skid_output(writebuf); + rc = fwrite(writebuf,len,1,o.logfd[fileidx]); + if (rc != 1) { + fprintf(stderr, "Failed to write %d bytes of data to (logt==%d) stream. fwrite returned %d. Quitting.\n", len, logt, rc); + exit(1); + } + } + break; + + default: + fprintf(stderr, "log_vwrite(): Passed unknown log type (%d). Note that this function, unlike log_write, can only handle one log type at a time (no bitmasks)\n", logt); + exit(1); + } + + return; +} + /* Write some information (printf style args) to the given log stream(s). Remember to watch out for format string bugs. */ void log_write(int logt, const char *fmt, ...) { va_list ap; - int i,l=logt,skid=1; - char b[4096]; - char *buf = b; - int bufsz = sizeof(b); - bool buf_alloced = false; - int rc = 0; + assert(logt > 0); - if (l & LOG_STDOUT) { - va_start(ap, fmt); - vfprintf(o.nmap_stdout, fmt, ap); - va_end(ap); - l-=LOG_STDOUT; - } - if (l & LOG_SKID_NOXLT) { skid=0; l -= LOG_SKID_NOXLT; l |= LOG_SKID; } - if (l<0 || l>LOG_MASK) return; - for (i=0;l;l>>=1,i++) - { - if (!o.logfd[i] || !(l&1)) continue; - while(1) { - va_start(ap, fmt); - rc = vsnprintf(buf,bufsz, fmt, ap); - va_end(ap); - if (rc >= 0 && rc < bufsz) - break; // Successful - // D'oh! Apparently not enough space - lets try a bigger buffer - bufsz = (rc > bufsz)? rc + 1 : bufsz * 2; - buf = (char *) safe_realloc(buf_alloced? buf : NULL, bufsz); - buf_alloced = true; - } - if (skid && ((1<LOG_MASK) return; + if (logt<0 || logt>LOG_FILE_MASK) return; for (i=0;logt;logt>>=1,i++) if (o.logfd[i] && (logt&1)) fclose(o.logfd[i]); } @@ -676,10 +761,16 @@ void log_flush(int logt) { fflush(o.nmap_stdout); logt -= LOG_STDOUT; } + + if (logt & LOG_STDERR) { + fflush(stderr); + logt -= LOG_STDERR; + } + if (logt & LOG_SKID_NOXLT) fatal("You are not allowed to log_flush() with LOG_SKID_NOXLT"); - if (logt<0 || logt>LOG_MASK) return; + if (logt<0 || logt>LOG_FILE_MASK) return; for (i=0;logt;logt>>=1,i++) { @@ -694,7 +785,7 @@ void log_flush(int logt) { void log_flush_all() { int fileno; - for(fileno = 0; fileno < LOG_TYPES; fileno++) { + for(fileno = 0; fileno < LOG_NUM_FILES; fileno++) { if (o.logfd[fileno]) fflush(o.logfd[fileno]); } fflush(stdout); @@ -707,7 +798,7 @@ void log_flush_all() { int log_open(int logt, int append, char *filename) { int i=0; - if (logt<=0 || logt>LOG_MASK) return -1; + if (logt<=0 || logt>LOG_FILE_MASK) return -1; while ((logt&1)==0) { i++; logt>>=1; } if (o.logfd[i]) fatal("Only one %s output filename allowed",logtypes[i]); if (*filename == '-' && *(filename + 1) == '\0') @@ -729,43 +820,6 @@ int log_open(int logt, int append, char *filename) return 1; } -/* Used in creating skript kiddie style output. |<-R4d! */ -void skid_output(char *s) -{ - int i; - for (i=0;s[i];i++) - if (rand()%2==0) - /* Substitutions commented out are not known to me, but maybe look nice */ - switch(s[i]) - { - case 'A': s[i]='4'; break; - /* case 'B': s[i]='8'; break; - case 'b': s[i]='6'; break; - case 'c': s[i]='k'; break; - case 'C': s[i]='K'; break; */ - case 'e': - case 'E': s[i]='3'; break; - case 'i': - case 'I': s[i]="!|1"[rand()%3]; break; - /* case 'k': s[i]='c'; break; - case 'K': s[i]='C'; break;*/ - case 'o': - case 'O': s[i]='0'; break; - case 's': - case 'S': - if (s[i+1] && !isalnum((int) s[i+1])) - s[i] = 'z'; - else s[i] = '$'; - break; - case 'z': s[i]='s'; break; - case 'Z': s[i]='S'; break; - } - else - { - if (s[i]>='A' && s[i]<='Z' && (rand()%3==0)) s[i]+='a'-'A'; - else if (s[i]>='a' && s[i]<='z' && (rand()%3==0)) s[i]-='a'-'A'; - } -} /* The items in ports should be in sequential order for space savings and easier to read output. Outputs @@ -786,7 +840,8 @@ char outpbuf[128]; strcat(outpbuf, ","); sprintf(outpbuf + strlen(outpbuf), "%hu", port); } - log_write(logt, "%s", outpbuf); + if (*outpbuf) + log_write(logt, "%s", outpbuf); range_start = port; } previous_port = port; diff --git a/output.h b/output.h index 39e69e060..f069fb717 100644 --- a/output.h +++ b/output.h @@ -106,17 +106,18 @@ #ifndef OUTPUT_H #define OUTPUT_H -#define LOG_TYPES 5 -#define LOG_MASK 31 +#define LOG_NUM_FILES 4 /* # of values that actual files (they must come first */ +#define LOG_FILE_MASK 15 /* The mask for log typs in the file array */ #define LOG_NORMAL 1 #define LOG_MACHINE 2 -#define LOG_HTML 4 -#define LOG_SKID 8 -#define LOG_XML 16 +#define LOG_SKID 4 +#define LOG_XML 8 #define LOG_STDOUT 1024 -#define LOG_SKID_NOXLT 2048 +#define LOG_STDERR 2048 +#define LOG_SKID_NOXLT 4096 +#define LOG_MAX LOG_SKID_NOXLT /* The maximum log type value */ -#define LOG_NAMES {"normal", "machine", "HTML", "$Cr!pT |