1
0
mirror of https://github.com/nmap/nmap.git synced 2025-12-06 04:31:29 +00:00

Add --resume from XML output. Closes #316. See #243

This commit is contained in:
dmiller
2016-12-06 02:55:55 +00:00
parent b87b0c7de0
commit b18d6fe5f7
7 changed files with 172 additions and 70 deletions

View File

@@ -1,5 +1,9 @@
# Nmap Changelog ($Id$); -*-text-*- # Nmap Changelog ($Id$); -*-text-*-
o [GH#316] Added scan resume from Nmap's XML output. Now you can --resume a
canceled scan from all 3 major output formats: -oN, -oG, and -oX.
[Tudor Emil Coman]
o [Ndiff][GH#591] Fix a bug where hosts with the same IP but different o [Ndiff][GH#591] Fix a bug where hosts with the same IP but different
hostnames were shown as changing hostnames between scans. Made sort stable hostnames were shown as changing hostnames between scans. Made sort stable
with regard to hostnames. [Daniel Miller] with regard to hostnames. [Daniel Miller]

View File

@@ -389,6 +389,7 @@ void NmapOps::Initialize() {
portlist = NULL; portlist = NULL;
exclude_portlist = NULL; exclude_portlist = NULL;
proxy_chain = NULL; proxy_chain = NULL;
resuming = false;
} }
bool NmapOps::SCTPScan() { bool NmapOps::SCTPScan() {

View File

@@ -181,6 +181,7 @@ class NmapOps {
/* Whether we have pcap functions (can be false on Windows). */ /* Whether we have pcap functions (can be false on Windows). */
bool have_pcap; bool have_pcap;
int debugging; int debugging;
bool resuming;
#define PACKET_SEND_NOPREF 1 #define PACKET_SEND_NOPREF 1
#define PACKET_SEND_ETH_WEAK 2 #define PACKET_SEND_ETH_WEAK 2

View File

@@ -222,6 +222,7 @@ int main(int argc, char *argv[]) {
if (gather_logfile_resumption_state(argv[2], &myargc, &myargv) == -1) { if (gather_logfile_resumption_state(argv[2], &myargc, &myargv) == -1) {
fatal("Cannot resume from (supposed) log file %s", argv[2]); fatal("Cannot resume from (supposed) log file %s", argv[2]);
} }
o.resuming = true;
return nmap_main(myargc, myargv); return nmap_main(myargc, myargv);
} }

148
nmap.cc
View File

@@ -1855,18 +1855,48 @@ int nmap_main(int argc, char *argv[]) {
fflush(stderr); fflush(stderr);
timep = time(NULL); timep = time(NULL);
/* Brief info in case they forget what was scanned */
Strncpy(mytime, ctime(&timep), sizeof(mytime)); Strncpy(mytime, ctime(&timep), sizeof(mytime));
chomp(mytime); chomp(mytime);
char *xslfname = o.XSLStyleSheet();
xml_start_document("nmaprun"); if (!o.resuming) {
if (xslfname) { /* Brief info in case they forget what was scanned */
xml_open_pi("xml-stylesheet"); char *xslfname = o.XSLStyleSheet();
xml_attribute("href", "%s", xslfname); xml_start_document("nmaprun");
xml_attribute("type", "text/xsl"); if (xslfname) {
xml_close_pi(); xml_open_pi("xml-stylesheet");
xml_attribute("href", "%s", xslfname);
xml_attribute("type", "text/xsl");
xml_close_pi();
xml_newline();
}
xml_start_comment();
xml_write_escaped(" %s %s scan initiated %s as: %s ", NMAP_NAME, NMAP_VERSION, mytime, join_quoted(argv, argc).c_str());
xml_end_comment();
xml_newline(); xml_newline();
xml_open_start_tag("nmaprun");
xml_attribute("scanner", "nmap");
xml_attribute("args", "%s", join_quoted(argv, argc).c_str());
xml_attribute("start", "%lu", (unsigned long) timep);
xml_attribute("startstr", "%s", mytime);
xml_attribute("version", "%s", NMAP_VERSION);
xml_attribute("xmloutputversion", NMAP_XMLOUTPUTVERSION);
xml_close_start_tag();
xml_newline();
output_xml_scaninfo_records(&ports);
xml_open_start_tag("verbose");
xml_attribute("level", "%d", o.verbose);
xml_close_empty_tag();
xml_newline();
xml_open_start_tag("debugging");
xml_attribute("level", "%d", o.debugging);
xml_close_empty_tag();
xml_newline();
} else {
xml_start_tag("nmaprun", false);
} }
std::string command; std::string command;
@@ -1877,37 +1907,11 @@ int nmap_main(int argc, char *argv[]) {
command += argv[i]; command += argv[i];
} }
xml_start_comment();
xml_write_escaped(" %s %s scan initiated %s as: %s ", NMAP_NAME, NMAP_VERSION, mytime, join_quoted(argv, argc).c_str());
xml_end_comment();
xml_newline();
log_write(LOG_NORMAL | LOG_MACHINE, "# "); log_write(LOG_NORMAL | LOG_MACHINE, "# ");
log_write(LOG_NORMAL | LOG_MACHINE, "%s %s scan initiated %s as: ", NMAP_NAME, NMAP_VERSION, mytime); log_write(LOG_NORMAL | LOG_MACHINE, "%s %s scan initiated %s as: ", NMAP_NAME, NMAP_VERSION, mytime);
log_write(LOG_NORMAL | LOG_MACHINE, "%s", command.c_str()); log_write(LOG_NORMAL | LOG_MACHINE, "%s", command.c_str());
log_write(LOG_NORMAL | LOG_MACHINE, "\n"); log_write(LOG_NORMAL | LOG_MACHINE, "\n");
xml_open_start_tag("nmaprun");
xml_attribute("scanner", "nmap");
xml_attribute("args", "%s", join_quoted(argv, argc).c_str());
xml_attribute("start", "%lu", (unsigned long) timep);
xml_attribute("startstr", "%s", mytime);
xml_attribute("version", "%s", NMAP_VERSION);
xml_attribute("xmloutputversion", NMAP_XMLOUTPUTVERSION);
xml_close_start_tag();
xml_newline();
output_xml_scaninfo_records(&ports);
xml_open_start_tag("verbose");
xml_attribute("level", "%d", o.verbose);
xml_close_empty_tag();
xml_newline();
xml_open_start_tag("debugging");
xml_attribute("level", "%d", o.debugging);
xml_close_empty_tag();
xml_newline();
/* Before we randomize the ports scanned, lets output them to machine /* Before we randomize the ports scanned, lets output them to machine
parseable output */ parseable output */
if (o.verbose) if (o.verbose)
@@ -2328,13 +2332,22 @@ int gather_logfile_resumption_state(char *fname, int *myargc, char ***myargv) {
if (!q || ((unsigned int) (q - p) >= sizeof(nmap_arg_buffer) - 32)) if (!q || ((unsigned int) (q - p) >= sizeof(nmap_arg_buffer) - 32))
fatal("Unable to parse supposed log file %s. Perhaps the Nmap execution had not finished at least one host? In that case there is no use \"resuming\"", fname); fatal("Unable to parse supposed log file %s. Perhaps the Nmap execution had not finished at least one host? In that case there is no use \"resuming\"", fname);
strncpy(nmap_arg_buffer, "nmap --append-output ", sizeof(nmap_arg_buffer)); strncpy(nmap_arg_buffer, "nmap --append-output ", sizeof(nmap_arg_buffer));
if ((q - p) + 21 + 1 >= (int) sizeof(nmap_arg_buffer)) if ((q - p) + 21 + 1 >= (int) sizeof(nmap_arg_buffer))
fatal("0verfl0w"); fatal("0verfl0w");
memcpy(nmap_arg_buffer + 21, p, q - p); memcpy(nmap_arg_buffer + 21, p, q - p);
nmap_arg_buffer[21 + q - p] = '\0'; nmap_arg_buffer[21 + q - p] = '\0';
q = strstr(nmap_arg_buffer, "-->");
if (q) {
*q = '\0';
char *unescaped = xml_unescape(nmap_arg_buffer);
if (sizeof(nmap_arg_buffer) < strlen(unescaped) + 1)
fatal("0verfl0w");
memcpy(nmap_arg_buffer, unescaped, strlen(unescaped) + 1);
free(unescaped);
}
if (strstr(nmap_arg_buffer, "--randomize-hosts") != NULL) { if (strstr(nmap_arg_buffer, "--randomize-hosts") != NULL) {
error("WARNING: You are attempting to resume a scan which used --randomize-hosts. Some hosts in the last randomized batch may be missed and others may be repeated once"); error("WARNING: You are attempting to resume a scan which used --randomize-hosts. Some hosts in the last randomized batch may be missed and others may be repeated once");
} }
@@ -2360,37 +2373,52 @@ int gather_logfile_resumption_state(char *fname, int *myargc, char ***myargv) {
fatal("Unable to parse supposed log file %s. Sorry", fname); fatal("Unable to parse supposed log file %s. Sorry", fname);
*q = ' '; *q = ' ';
} else { } else {
/* OK, I guess (hope) it is a normal log then (-oN) */ /* Let's see if it's an XML log (-oX) */
q = p; q = p;
found = NULL; found = NULL;
while ((q = strstr(q, "\nNmap scan report for "))) while ((q = strstr(q, "\n<address addr=\"")))
found = q = q + 22; found = q = q + 16;
/* There may be some later IPs of the form :
"Nmap scan report for florence (x.x.7.10)" (dns reverse lookup)
or "Nmap scan report for x.x.7.10".
*/
if (found) { if (found) {
q = strchr(found, '\n'); q = strchr(found, '"');
if (!q) if (!q)
fatal("Unable to parse supposed log file %s. Sorry", fname); fatal("Unable to parse supposed log file %s. Sorry", fname);
*q = '\0'; *q = '\0';
p = strchr(found, '('); if (inet_pton(AF_INET, found, &lastip) == 0)
if (!p) { /* No DNS reverse lookup, found should already contain IP */ fatal("Unable to parse supposed log file %s. Sorry", fname);
lastipstr = strdup(found); *q = '"';
} else { /* DNS reverse lookup, IP is between parentheses */
*q = '\n';
q--;
*q = '\0';
lastipstr = strdup(p + 1);
}
*q = p ? ')' : '\n'; /* recover changed chars */
if (inet_pton(AF_INET, lastipstr, &lastip) == 0)
fatal("Unable to parse ip (%s) in supposed log file %s. Sorry", lastipstr, fname);
free(lastipstr);
} else { } else {
error("Warning: You asked for --resume but it doesn't look like any hosts in the log file were successfully scanned. Starting from the beginning."); /* OK, I guess (hope) it is a normal log then (-oN) */
lastip.s_addr = 0; q = p;
found = NULL;
while ((q = strstr(q, "\nNmap scan report for ")))
found = q = q + 22;
/* There may be some later IPs of the form :
"Nmap scan report for florence (x.x.7.10)" (dns reverse lookup)
or "Nmap scan report for x.x.7.10".
*/
if (found) {
q = strchr(found, '\n');
if (!q)
fatal("Unable to parse supposed log file %s. Sorry", fname);
*q = '\0';
p = strchr(found, '(');
if (!p) { /* No DNS reverse lookup, found should already contain IP */
lastipstr = strdup(found);
} else { /* DNS reverse lookup, IP is between parentheses */
*q = '\n';
q--;
*q = '\0';
lastipstr = strdup(p + 1);
}
*q = p ? ')' : '\n'; /* recover changed chars */
if (inet_pton(AF_INET, lastipstr, &lastip) == 0)
fatal("Unable to parse ip (%s) in supposed log file %s. Sorry", lastipstr, fname);
free(lastipstr);
} else {
error("Warning: You asked for --resume but it doesn't look like any hosts in the log file were successfully scanned. Starting from the beginning.");
lastip.s_addr = 0;
}
} }
} }
o.resume_ip = lastip; o.resume_ip = lastip;

78
xml.cc
View File

@@ -201,6 +201,68 @@ struct xml_writer {
static struct xml_writer xml; static struct xml_writer xml;
char *xml_unescape(const char *str) {
char *result = NULL;
size_t n = 0, len;
const char *p;
int i;
i = 0;
for (p = str; *p != '\0'; p++) {
const char *repl;
char buf[32];
if (*p != '&') {
/* Based on the asumption that ampersand is only used for escaping. */
buf[0] = *p;
buf[1] = '\0';
repl = buf;
} else if (strncmp(p, "&lt;", 4) == 0) {
repl = "<";
p += 3;
} else if (strncmp(p, "&gt;", 4) == 0) {
repl = ">";
p += 3;
} else if (strncmp(p, "&amp;", 5) == 0) {
repl = "&";
p += 4;
} else if (strncmp(p, "&quot;", 6) == 0) {
repl = "\"";
p += 5;
} else if (strncmp(p, "&apos;", 6) == 0) {
repl = "\'";
p += 5;
} else if (strncmp(p, "&#45;", 5) == 0) {
repl = "-";
p += 4;
} else {
/* Escaped control characters and anything outside of ASCII. */
Strncpy(buf, p + 3, sizeof(buf));
char *q;
q = strchr(buf, ';');
if(!q)
buf[0] = '\0';
else
*q = '\0';
repl = buf;
}
len = strlen(repl);
/* Double the size of the result buffer if necessary. */
if (i == 0 || i + len > n) {
n = (i + len) * 2;
result = (char *) safe_realloc(result, n + 1);
}
memcpy(result + i, repl, len);
i += len;
}
/* Trim to length. (Also does initial allocation when str is empty.) */
result = (char *) safe_realloc(result, i + 1);
result[i] = '\0';
return result;
}
/* Escape a string for inclusion in XML. This gets <>&, "' for attribute /* Escape a string for inclusion in XML. This gets <>&, "' for attribute
values, -- for inside comments, and characters with value > 0x7F. It values, -- for inside comments, and characters with value > 0x7F. It
also gets control characters with value < 0x20 to avoid parser also gets control characters with value < 0x20 to avoid parser
@@ -362,9 +424,10 @@ int xml_close_pi() {
/* Open a start tag, like "<name". The tag must be later closed with /* Open a start tag, like "<name". The tag must be later closed with
xml_close_start_tag or xml_close_empty_tag. Usually the tag is closed xml_close_start_tag or xml_close_empty_tag. Usually the tag is closed
after writing some attributes. */ after writing some attributes. */
int xml_open_start_tag(const char *name) { int xml_open_start_tag(const char *name, const bool write) {
assert(!xml.tag_open); assert(!xml.tag_open);
log_write(LOG_XML, "<%s", name); if (write)
log_write(LOG_XML, "<%s", name);
xml.element_stack.push_back(name); xml.element_stack.push_back(name);
xml.tag_open = true; xml.tag_open = true;
xml.root_written = true; xml.root_written = true;
@@ -372,9 +435,10 @@ int xml_open_start_tag(const char *name) {
return 0; return 0;
} }
int xml_close_start_tag() { int xml_close_start_tag(const bool write) {
assert(xml.tag_open); assert(xml.tag_open);
log_write(LOG_XML, ">"); if(write)
log_write(LOG_XML, ">");
xml.tag_open = false; xml.tag_open = false;
return 0; return 0;
@@ -392,10 +456,10 @@ int xml_close_empty_tag() {
return 0; return 0;
} }
int xml_start_tag(const char *name) { int xml_start_tag(const char *name, const bool write) {
if (xml_open_start_tag(name) < 0) if (xml_open_start_tag(name, write) < 0)
return -1; return -1;
if (xml_close_start_tag() < 0) if (xml_close_start_tag(write) < 0)
return -1; return -1;
return 0; return 0;

9
xml.h
View File

@@ -137,10 +137,10 @@ int xml_end_comment();
int xml_open_pi(const char *name); int xml_open_pi(const char *name);
int xml_close_pi(); int xml_close_pi();
int xml_open_start_tag(const char *name); int xml_open_start_tag(const char *name, const bool write = true);
int xml_close_start_tag(); int xml_close_start_tag(const bool write = true);
int xml_close_empty_tag(); int xml_close_empty_tag();
int xml_start_tag(const char *name); int xml_start_tag(const char *name, const bool write = true);
int xml_end_tag(); int xml_end_tag();
int xml_attribute(const char *name, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); int xml_attribute(const char *name, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
@@ -151,5 +151,8 @@ int xml_depth();
bool xml_tag_open(); bool xml_tag_open();
bool xml_root_written(); bool xml_root_written();
char *xml_unescape(const char *str);
#endif #endif