diff --git a/output.cc b/output.cc index f116f6537..4303606ce 100644 --- a/output.cc +++ b/output.cc @@ -494,6 +494,9 @@ static char *formatScriptOutput(const ScriptResult &sr) { } #endif /* NOLUA */ +/* Output a list of ports, compressing ranges like 80-85 */ +static void output_rangelist_given_ports(int logt, unsigned short *ports, int numports); + /* Prints the familiar Nmap tabular output showing the "interesting" ports found on the machine. It also handles the Machine/Grepable output and the XML output. It is pretty ugly -- in particular I @@ -523,6 +526,7 @@ void printportoutput(Target *currenths, PortList *plist) { int numrows; int numignoredports = plist->numIgnoredPorts(); int numports = plist->numPorts(); + state_reason_summary_t *reasons, *currentr; std::vector saved_servicefps; @@ -530,53 +534,8 @@ void printportoutput(Target *currenths, PortList *plist) { return; xml_start_tag("ports"); - int prevstate = PORT_UNKNOWN; - int istate; - - while ((istate = plist->nextIgnoredState(prevstate)) != PORT_UNKNOWN) { - xml_open_start_tag("extraports"); - xml_attribute("state", "%s", statenum2str(istate)); - xml_attribute("count", "%d", plist->getStateCounts(istate)); - xml_close_start_tag(); - xml_newline(); - print_xml_state_summary(plist, istate); - xml_end_tag(); - xml_newline(); - prevstate = istate; - } - - if (numignoredports == numports) { - log_write(LOG_PLAIN, "All %d scanned ports on %s are ", - numignoredports, - currenths->NameIP(hostname, sizeof(hostname))); - log_write(LOG_MACHINE, "Host: %s (%s)\t%s: ", - currenths->targetipstr(), currenths->HostName(), - (o.ipprotscan) ? "Protocols" : "Ports"); - - if (plist->numIgnoredStates() == 1) { - istate = plist->nextIgnoredState(PORT_UNKNOWN); - log_write(LOG_PLAIN, "%s", statenum2str(istate)); - /* Grepable output supports only one ignored state. */ - log_write(LOG_MACHINE, "\tIgnored State: %s (%d)", - statenum2str(istate), plist->getStateCounts(istate)); - } else { - prevstate = PORT_UNKNOWN; - while ((istate = plist->nextIgnoredState(prevstate)) != PORT_UNKNOWN) { - if (prevstate != PORT_UNKNOWN) - log_write(LOG_PLAIN, " or "); - log_write(LOG_PLAIN, "%s (%d)", statenum2str(istate), - plist->getStateCounts(istate)); - prevstate = istate; - } - } - if (o.reason) - print_state_summary(plist, STATE_REASON_EMPTY); - log_write(LOG_PLAIN, "\n"); - - xml_end_tag(); /* ports */ - xml_newline(); - return; - } + log_write(LOG_MACHINE, "Host: %s (%s)", currenths->targetipstr(), + currenths->HostName()); if ((o.verbose > 1 || o.debugging) && currenths->StartTime()) { time_t tm_secs, tm_sece; @@ -603,35 +562,82 @@ void printportoutput(Target *currenths, PortList *plist) { } } } - log_write(LOG_MACHINE, "Host: %s (%s)", currenths->targetipstr(), - currenths->HostName()); - /* Show line like: - Not shown: 3995 closed ports, 514 filtered ports - if appropriate (note that states are reverse-sorted by # of ports) */ - prevstate = PORT_UNKNOWN; + int prevstate = PORT_UNKNOWN; + int istate; + while ((istate = plist->nextIgnoredState(prevstate)) != PORT_UNKNOWN) { - if (prevstate == PORT_UNKNOWN) + i = plist->getStateCounts(istate); + xml_open_start_tag("extraports"); + xml_attribute("state", "%s", statenum2str(istate)); + xml_attribute("count", "%d", i); + xml_close_start_tag(); + xml_newline(); + + /* Show line like: + Not shown: 98 open|filtered udp ports (no-response), 59 closed tcp ports (reset) + if appropriate (note that states are reverse-sorted by # of ports) */ + if (prevstate == PORT_UNKNOWN) { + // First time through, check special case + if (numignoredports == numports) { + log_write(LOG_PLAIN, "All %d scanned ports on %s are in ignored states.\n", + numignoredports, currenths->NameIP(hostname, sizeof(hostname))); + log_write(LOG_MACHINE, "\t%s: ", (o.ipprotscan) ? "Protocols" : "Ports"); + /* Grepable output supports only one ignored state. */ + if (plist->numIgnoredStates() == 1) { + log_write(LOG_MACHINE, "\tIgnored State: %s (%d)", statenum2str(istate), i); + } + } log_write(LOG_PLAIN, "Not shown: "); - else + } else { log_write(LOG_PLAIN, ", "); - char desc[32]; - if (o.ipprotscan) - Snprintf(desc, sizeof(desc), - (plist->getStateCounts(istate) == - 1) ? "protocol" : "protocols"); - else - Snprintf(desc, sizeof(desc), - (plist->getStateCounts(istate) == 1) ? "port" : "ports"); - log_write(LOG_PLAIN, "%d %s %s", plist->getStateCounts(istate), - statenum2str(istate), desc); + } + + if((currentr = reasons = get_state_reason_summary(plist, istate)) == NULL) { + log_write(LOG_PLAIN, "%d %s %s%s", i, statenum2str(istate), + o.ipprotscan ? "protocol" : "port", + plist->getStateCounts(istate) == 1 ? "" : "s"); + prevstate = istate; + continue; + } + + while(currentr != NULL) { + if(currentr->count > 0) { + xml_open_start_tag("extrareasons"); + xml_attribute("reason", "%s", reason_str(currentr->reason_id, SINGULAR)); + xml_attribute("count", "%d", currentr->count); + xml_attribute("proto", "%s", IPPROTO2STR(currentr->proto)); + xml_write_raw(" ports=\""); + output_rangelist_given_ports(LOG_XML, currentr->ports, currentr->count); + xml_write_raw("\""); + xml_close_empty_tag(); + xml_newline(); + + if (currentr != reasons) + log_write(LOG_PLAIN, ", "); + log_write(LOG_PLAIN, "%d %s %s %s%s (%s)", + currentr->count, statenum2str(istate), IPPROTO2STR(currentr->proto), + o.ipprotscan ? "protocol" : "port", + plist->getStateCounts(istate) == 1 ? "" : "s", + reason_str(currentr->reason_id, SINGULAR)); + } + currentr = currentr->next; + } + state_reason_summary_dinit(reasons); + xml_end_tag(); + xml_newline(); prevstate = istate; } log_write(LOG_PLAIN, "\n"); - if (o.reason) - print_state_summary(plist, STATE_REASON_FULL); + if (numignoredports == numports) { + // Nothing left to show. + xml_end_tag(); /* ports */ + xml_newline(); + log_flush_all(); + return; + } /* OK, now it is time to deal with the service table ... */ colno = 0; diff --git a/portreasons.cc b/portreasons.cc index 5a58dbdbe..bf177f26f 100644 --- a/portreasons.cc +++ b/portreasons.cc @@ -67,11 +67,9 @@ #include "winfix.h" #endif #include "portlist.h" -#include "output.h" #include "NmapOps.h" #include "portreasons.h" #include "Target.h" -#include "xml.h" extern NmapOps o; @@ -252,7 +250,7 @@ static void state_reason_summary_init(state_reason_summary_t *r) { r->next = NULL; } -static void state_reason_summary_dinit(state_reason_summary_t *r) { +void state_reason_summary_dinit(state_reason_summary_t *r) { state_reason_summary_t *tmp; while(r != NULL) { @@ -262,19 +260,6 @@ static void state_reason_summary_dinit(state_reason_summary_t *r) { } } -/* Counts how different valid state reasons exist */ -static int state_summary_size(state_reason_summary_t *head) { - state_reason_summary_t *current = head; - int size = 0; - - while(current) { - if(current->count > 0) - size++; - current = current->next; - } - return size; -} - /* Simon Tatham's linked list merge sort * * Merge sort works really well on linked lists @@ -333,28 +318,29 @@ static state_reason_summary_t *reason_sort(state_reason_summary_t *list) { } /* Builds and aggregates reason state summary messages */ -static int update_state_summary(state_reason_summary_t *head, reason_t reason_id) { +static int update_state_summary(state_reason_summary_t *head, Port *port) { state_reason_summary_t *tmp = head; if(tmp == NULL) return -1; while(1) { - if(tmp->reason_id == reason_id) { - tmp->count++; - return 0; + if(tmp->reason_id == port->reason.reason_id && tmp->proto == port->proto) { + break; } if(tmp->next == NULL) { tmp->next = (state_reason_summary_t *)safe_malloc(sizeof(state_reason_summary_t)); tmp = tmp->next; + state_reason_summary_init(tmp); + tmp->reason_id = port->reason.reason_id; + tmp->proto = port->proto; break; } tmp = tmp->next; } - state_reason_summary_init(tmp); - tmp->reason_id = reason_id; - tmp->count = 1; + tmp->ports[tmp->count] = port->portno; + tmp->count++; return 0; } @@ -374,14 +360,14 @@ static unsigned int get_state_summary(state_reason_summary_t *head, PortList *Po while((current = Ports->nextPort(current, &port, proto, state)) != NULL) { if(Ports->isIgnoredState(current->state, NULL)) { total++; - update_state_summary(reason, current->reason.reason_id); + update_state_summary(reason, current); } } return total; } /* parse and sort reason summary for main print_* functions */ -static state_reason_summary_t *print_state_summary_internal(PortList *Ports, int state) { +state_reason_summary_t *get_state_reason_summary(PortList *Ports, int state) { state_reason_summary_t *reason_head; reason_head = (state_reason_summary_t *)safe_malloc(sizeof(state_reason_summary_t)); @@ -416,63 +402,6 @@ void state_reason_init(state_reason_t *reason) { reason->ttl = 0; } -/* Main external interface to converting, building, sorting and - * printing plain-text state reason summaries */ -void print_state_summary(PortList *Ports, unsigned short type) { - state_reason_summary_t *reason_head, *currentr; - bool first_time = true; - const char *separator = ", "; - int states; - - if((reason_head = print_state_summary_internal(Ports, 0)) == NULL) - return; - - if(type == STATE_REASON_EMPTY) - log_write(LOG_PLAIN, " because of"); - else if(type == STATE_REASON_FULL) - log_write(LOG_PLAIN, "Reason:"); - else - assert(0); - - states = state_summary_size(reason_head); - currentr = reason_head; - - while(currentr != NULL) { - if(states == 1 && (!first_time)) - separator = " and "; - if(currentr->count > 0) { - log_write(LOG_PLAIN, "%s%d %s", (first_time) ? " " : separator, - currentr->count, reason_str(currentr->reason_id, currentr->count)); - first_time = false; - - } - states--; - currentr = currentr->next; - } - if(type == STATE_REASON_FULL) - log_write(LOG_PLAIN, "\n"); - state_reason_summary_dinit(reason_head); -} - -void print_xml_state_summary(PortList *Ports, int state) { - state_reason_summary_t *reason_head, *currentr; - - if((currentr = reason_head = print_state_summary_internal(Ports, state)) == NULL) - return; - - while(currentr != NULL) { - if(currentr->count > 0) { - xml_open_start_tag("extrareasons"); - xml_attribute("reason", "%s", reason_str(currentr->reason_id, currentr->count)); - xml_attribute("count", "%d", currentr->count); - xml_close_empty_tag(); - xml_newline(); - } - currentr = currentr->next; - } - state_reason_summary_dinit(reason_head); -} - /* converts target into reason message for ping scans. Uses a static * buffer so new values overwrite old values */ char *target_reason_str(Target *t) { diff --git a/portreasons.h b/portreasons.h index 76cfb0c6e..3bb1529fd 100644 --- a/portreasons.h +++ b/portreasons.h @@ -107,6 +107,8 @@ typedef struct port_reason_summary { reason_t reason_id; unsigned int count; struct port_reason_summary *next; + unsigned short proto; + unsigned short ports[0xffff+1]; } state_reason_summary_t; @@ -146,12 +148,6 @@ public: /* Function to translate ICMP code and typ to reason code */ reason_codes icmp_to_reason(u8 proto, int icmp_type, int icmp_code); -/* passed to the print_state_summary. - * STATE_REASON_EMPTY will append to the current line, prefixed with " because of" - * STATE_REASON_FULL will start a new line, prefixed with "Reason:" */ -#define STATE_REASON_EMPTY 0 -#define STATE_REASON_FULL 1 - /* Passed to reason_str to determine if string should be in * plural of singular form */ #define SINGULAR 1 @@ -164,9 +160,10 @@ void state_reason_init(state_reason_t *reason); * port the plural is used, otherwise the singular is used. */ const char *reason_str(reason_t reason_id, unsigned int number); -/* Displays reason summary messages */ -void print_state_summary(PortList *Ports, unsigned short type); -void print_xml_state_summary(PortList *Ports, int state); +/* Returns a linked list of reasons why ports are in a given state */ +state_reason_summary_t *get_state_reason_summary(PortList *Ports, int state); +/* Frees the linked list from get_state_reason_summary */ +void state_reason_summary_dinit(state_reason_summary_t *r); /* Build an output string based on reason and source ip address. * Uses static return value so previous values will be over