new (at least rejiggered) output system, --log-errors option, ready for 4.02Alpha1, I think

This commit is contained in:
fyodor 2006-03-04 04:17:32 +00:00
parent 848ad2a96a
commit 705123dd29
11 changed files with 260 additions and 134 deletions

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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 <filename>: Resume an aborted scan
--stylesheet <path/URL>: XSL stylesheet to transform XML output to HTML

View file

@ -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 <filename>: Resume an aborted scan\n"
" --stylesheet <path/URL>: XSL stylesheet to transform XML output to HTML\n"

View file

@ -99,6 +99,10 @@
/* $Id$ */
#include "nmap_error.h"
#include "output.h"
#include "NmapOps.h"
extern NmapOps o;
#ifdef WIN32
#include <windows.h>
@ -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;
}

View file

@ -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;
}

View file

@ -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 */

207
output.cc
View file

@ -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 <service> 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<<i)&LOG_SKID)) skid_output(buf);
fwrite(buf,1,strlen(buf),o.logfd[i]);
if (!fmt || !*fmt) return;
for (int l = 1; l <= LOG_MAX; l <<= 1) {
if (logt & l) {
va_start(ap, fmt);
log_vwrite(l, fmt, ap);
va_end(ap);
}
if (buf_alloced)
free(buf);
}
return;
}
/* Close the given log stream(s) */
void log_close(int logt)
{
int i;
if (logt<0 || logt>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;

View file

@ -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 |<!dd!3", "XML"}
#define LOG_NAMES {"normal", "machine", "$Cr!pT |<!dd!3", "XML"}
#include "portlist.h"
#include "tcpip.h"
@ -143,6 +144,14 @@ void print_MAC_XML_Info(Target *currenths);
void log_write(int logt, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3)));
/* 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);
/* Close the given log stream(s) */
void log_close(int logt);