diff --git a/idle_scan.cc b/idle_scan.cc index 8e17c496d..492186a9a 100644 --- a/idle_scan.cc +++ b/idle_scan.cc @@ -926,7 +926,7 @@ static int idle_treescan(struct idle_proxy_info *proxy, Target *target, /* If our first count erroneously found and added an open port, we must delete it */ if (firstHalfSz == 1 && flatcount1 == 1 && retrycount == 0) - target->ports.removePort(ports[0], IPPROTO_TCP); + target->ports.forgetPort(ports[0], IPPROTO_TCP); } } @@ -952,7 +952,7 @@ static int idle_treescan(struct idle_proxy_info *proxy, Target *target, /* If our first count erroneously found and added an open port, we must delete it */ if (secondHalfSz == 1 && flatcount2 == 1 && retrycount == 0) - target->ports.removePort(ports[firstHalfSz], IPPROTO_TCP); + target->ports.forgetPort(ports[firstHalfSz], IPPROTO_TCP); } @@ -960,10 +960,10 @@ static int idle_treescan(struct idle_proxy_info *proxy, Target *target, } if (firstHalfSz == 1 && flatcount1 == 1) - target->ports.addPort(ports[0], IPPROTO_TCP, PORT_OPEN); + target->ports.setPortState(ports[0], IPPROTO_TCP, PORT_OPEN); if ((secondHalfSz == 1) && flatcount2 == 1) - target->ports.addPort(ports[firstHalfSz], IPPROTO_TCP, PORT_OPEN); + target->ports.setPortState(ports[firstHalfSz], IPPROTO_TCP, PORT_OPEN); return totalfound; } @@ -1043,8 +1043,8 @@ void idle_scan(Target *target, u16 *portarray, int numports, /* Now we go through the ports which were scanned but not determined to be open, and add them in the "closed" state */ for(portidx = 0; portidx < numports; portidx++) { - if (target->ports.getPortEntry(portarray[portidx], IPPROTO_TCP) == NULL) { - target->ports.addPort(portarray[portidx], IPPROTO_TCP, PORT_CLOSEDFILTERED); + if (target->ports.getPortState(portarray[portidx], IPPROTO_TCP) == -1) { + target->ports.setPortState(portarray[portidx], IPPROTO_TCP, PORT_CLOSEDFILTERED); target->ports.setStateReason(portarray[portidx], IPPROTO_TCP, ER_NOIPIDCHANGE, 0, 0); } else target->ports.setStateReason(portarray[portidx], IPPROTO_TCP, ER_IPIDCHANGE, 0, 0); diff --git a/nmap_rpc.cc b/nmap_rpc.cc index a234715f3..d043ffdf8 100644 --- a/nmap_rpc.cc +++ b/nmap_rpc.cc @@ -361,7 +361,7 @@ static int rpc_are_we_done(char *msg, int msg_len, Target *target, if (rsi->rpc_current_port->state == PORT_OPENFILTERED) { /* Received a packet, so this port is actually open */ - target->ports.addPort(rsi->rpc_current_port->portno, + target->ports.setPortState(rsi->rpc_current_port->portno, rsi->rpc_current_port->proto, PORT_OPEN); } diff --git a/nmap_rpc.h b/nmap_rpc.h index 5d0d96fc7..8874ce976 100644 --- a/nmap_rpc.h +++ b/nmap_rpc.h @@ -147,7 +147,7 @@ struct rpc_info { }; struct rpcscaninfo { - Port *rpc_current_port; + const Port *rpc_current_port; unsigned long *rpc_progs; unsigned long rpc_number; int valid_responses_this_port; /* Number of valid (RPC wise) responses we diff --git a/nse_main.cc b/nse_main.cc index 868a470eb..2fe96e4c2 100644 --- a/nse_main.cc +++ b/nse_main.cc @@ -82,13 +82,14 @@ static int ports (lua_State *L) Target *target = get_target(L, 1); PortList *plist = &(target->ports); Port *current = NULL; + Port port; lua_newtable(L); for (int i = 0; states[i] != PORT_HIGHEST_STATE; i++) - while ((current = plist->nextPort(current, TCPANDUDPANDSCTP, + while ((current = plist->nextPort(current, &port, TCPANDUDPANDSCTP, states[i])) != NULL) { lua_newtable(L); - set_portinfo(L, current); + set_portinfo(L, target, current); lua_pushboolean(L, 1); lua_rawset(L, -3); } @@ -112,10 +113,10 @@ static int port_set_output (lua_State *L) { ScriptResult sr; Target *target = get_target(L, 1); - Port *port = get_port(L, target, 2); + const Port *port = get_port(L, target, 2); sr.set_id(luaL_checkstring(L, 3)); sr.set_output(luaL_checkstring(L, 4)); - port->scriptResults.push_back(sr); + target->ports.addScriptResult(port->portno, port->proto, sr); /* increment host port script results*/ target->ports.numscriptresults++; return 0; @@ -247,7 +248,7 @@ void ScriptResult::set_output (const char *out) output = std::string(out); } -std::string ScriptResult::get_output (void) +std::string ScriptResult::get_output (void) const { return output; } @@ -257,7 +258,7 @@ void ScriptResult::set_id (const char *ident) id = std::string(ident); } -std::string ScriptResult::get_id (void) +std::string ScriptResult::get_id (void) const { return id; } diff --git a/nse_main.h b/nse_main.h index 0e74d812e..bc5fdde89 100644 --- a/nse_main.h +++ b/nse_main.h @@ -20,9 +20,9 @@ class ScriptResult std::string id; public: void set_output (const char *); - std::string get_output (void); + std::string get_output (void) const; void set_id (const char *); - std::string get_id (void); + std::string get_id (void) const; }; typedef std::vector ScriptResults; diff --git a/nse_nmaplib.cc b/nse_nmaplib.cc index 6a2cb03b0..ce4329292 100644 --- a/nse_nmaplib.cc +++ b/nse_nmaplib.cc @@ -28,20 +28,20 @@ extern "C" { extern NmapOps o; -void set_version(lua_State *L, struct serviceDeductions sd) { - SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.name, "name"); +void set_version(lua_State *L, const struct serviceDeductions *sd) { + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd->name, "name"); - lua_pushnumber(L, sd.name_confidence); + lua_pushnumber(L, sd->name_confidence); lua_setfield(L, -2, "name_confidence"); - SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.product, "product"); - SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.version, "version"); - SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.extrainfo, "extrainfo"); - SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.hostname, "hostname"); - SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.ostype, "ostype"); - SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.devicetype, "devicetype"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd->product, "product"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd->version, "version"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd->extrainfo, "extrainfo"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd->hostname, "hostname"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd->ostype, "ostype"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd->devicetype, "devicetype"); - switch(sd.service_tunnel) { + switch(sd->service_tunnel) { case(SERVICE_TUNNEL_NONE): SCRIPT_ENGINE_PUSHSTRING_NOTNULL("none", "service_tunnel"); break; @@ -54,9 +54,9 @@ void set_version(lua_State *L, struct serviceDeductions sd) { break; } - SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd.service_fp, "service_fp"); + SCRIPT_ENGINE_PUSHSTRING_NOTNULL(sd->service_fp, "service_fp"); - switch(sd.dtype) { + switch(sd->dtype) { case(SERVICE_DETECTION_TABLE): SCRIPT_ENGINE_PUSHSTRING_NOTNULL("table", "service_fp"); break; @@ -69,7 +69,7 @@ void set_version(lua_State *L, struct serviceDeductions sd) { break; } - switch(sd.rpc_status) { + switch(sd->rpc_status) { case(RPC_STATUS_UNTESTED): SCRIPT_ENGINE_PUSHSTRING_NOTNULL("untested", "rpc_status"); break; @@ -88,14 +88,14 @@ void set_version(lua_State *L, struct serviceDeductions sd) { break; } - if(sd.rpc_status == RPC_STATUS_GOOD_PROG) { - lua_pushnumber(L, sd.rpc_program); + if(sd->rpc_status == RPC_STATUS_GOOD_PROG) { + lua_pushnumber(L, sd->rpc_program); lua_setfield(L, -2, "rpc_program"); - lua_pushnumber(L, sd.rpc_lowver); + lua_pushnumber(L, sd->rpc_lowver); lua_setfield(L, -2, "rpc_lowver"); - lua_pushnumber(L, sd.rpc_highver); + lua_pushnumber(L, sd->rpc_highver); lua_setfield(L, -2, "rpc_highver"); } } @@ -103,24 +103,26 @@ void set_version(lua_State *L, struct serviceDeductions sd) { /* set some port state information onto the * table which is currently on the stack * */ -void set_portinfo(lua_State *L, Port* port) { +void set_portinfo(lua_State *L, const Target *target, const Port *port) { struct serviceDeductions sd; - port->getServiceDeductions(&sd); + target->ports.getServiceDeductions(port->portno, port->proto, &sd); lua_pushnumber(L, (double) port->portno); lua_setfield(L, -2, "number"); - lua_pushstring(L, sd.name); - lua_setfield(L, -2, "service"); + if (sd.name != NULL) { + lua_pushstring(L, sd.name); + lua_setfield(L, -2, "service"); + } + + lua_newtable(L); + set_version(L, &sd); + lua_setfield(L, -2, "version"); lua_pushstring(L, IPPROTO2STR(port->proto)); lua_setfield(L, -2, "protocol"); - lua_newtable(L); - set_version(L, sd); - lua_setfield(L, -2, "version"); - lua_pushstring(L, statenum2str(port->state)); lua_setfield(L, -2, "state"); @@ -419,9 +421,10 @@ done: return target; } -Port *get_port (lua_State *L, Target *target, int index) +const Port *get_port (lua_State *L, Target *target, int index) { - Port *port = NULL; + Port *p = NULL; + Port port; int portno, protocol; luaL_checktype(L, index, LUA_TTABLE); lua_getfield(L, index, "number"); @@ -435,11 +438,11 @@ Port *get_port (lua_State *L, Target *target, int index) strcmp(lua_tostring(L, -1), "udp") == 0 ? IPPROTO_UDP : strcmp(lua_tostring(L, -1), "sctp") == 0 ? IPPROTO_SCTP : luaL_error(L, "port 'protocol' field must be \"udp\", \"sctp\" or \"tcp\""); - while ((port = target->ports.nextPort(port, protocol, PORT_UNKNOWN)) != NULL) - if (port->portno == portno) + while ((p = target->ports.nextPort(p, &port, protocol, PORT_UNKNOWN)) != NULL) + if (p->portno == portno) break; lua_pop(L, 2); - return port; + return p; } /* this function can be called from lua to obtain the port state @@ -455,7 +458,7 @@ Port *get_port (lua_State *L, Target *target, int index) static int l_get_port_state (lua_State *L) { Target *target; - Port *port; + const Port *port; target = get_target(L, 1); port = get_port(L, target, 2); if (port == NULL) @@ -463,7 +466,7 @@ static int l_get_port_state (lua_State *L) else { lua_newtable(L); - set_portinfo(L, port); + set_portinfo(L, target, port); } return 1; } @@ -477,7 +480,7 @@ static int l_set_port_state (lua_State *L) static const int opstate[] = {PORT_OPEN, PORT_CLOSED}; static const char *op[] = {"open", "closed", NULL}; Target *target; - Port *port; + const Port *port; target = get_target(L, 1); if ((port = get_port(L, target, 2)) != NULL) { @@ -486,17 +489,15 @@ static int l_set_port_state (lua_State *L) case PORT_OPEN: if (port->state == PORT_OPEN) return 0; - target->ports.addPort(port->portno, port->proto, PORT_OPEN); - port->state = PORT_OPEN; + target->ports.setPortState(port->portno, port->proto, PORT_OPEN); break; case PORT_CLOSED: if (port->state == PORT_CLOSED) return 0; - target->ports.addPort(port->portno, port->proto, PORT_CLOSED); - port->state = PORT_CLOSED; + target->ports.setPortState(port->portno, port->proto, PORT_CLOSED); break; } - port->reason.reason_id = ER_SCRIPT; + target->ports.setStateReason(port->portno, port->proto, ER_SCRIPT, 0, 0); } return 0; } @@ -518,7 +519,7 @@ static int l_set_port_version (lua_State *L) "incomplete" }; Target *target; - Port *port; + const Port *port; enum service_tunnel_type tunnel = SERVICE_TUNNEL_NONE; enum serviceprobestate probestate = opversion[luaL_checkoption(L, 3, "hardmatched", ops)]; @@ -549,10 +550,12 @@ static int l_set_port_version (lua_State *L) luaL_argerror(L, 2, "invalid value for port.version.service_tunnel"); if (o.servicescan) - port->setServiceProbeResults(probestate, name, tunnel, product, + target->ports.setServiceProbeResults(port->portno, port->proto, + probestate, name, tunnel, product, version, extrainfo, hostname, ostype, devicetype, NULL); else - port->setServiceProbeResults(probestate, name, tunnel, NULL, NULL, + target->ports.setServiceProbeResults(port->portno, port->proto, + probestate, name, tunnel, NULL, NULL, NULL, NULL, NULL, NULL, NULL); return 0; diff --git a/nse_nmaplib.h b/nse_nmaplib.h index 477287b59..3020a05cc 100644 --- a/nse_nmaplib.h +++ b/nse_nmaplib.h @@ -7,9 +7,9 @@ class Port; int luaopen_nmap(lua_State* l); int luaopen_stdnse_c (lua_State *L); void set_hostinfo(lua_State* l, Target* currenths); -void set_portinfo(lua_State* l, Port* port); +void set_portinfo(lua_State* l, const Target *target, const Port* port); Target *get_target (lua_State *L, int index); -Port *get_port (lua_State *L, Target *target, int index); +const Port *get_port (lua_State *L, Target *target, int index); #endif diff --git a/osscan2.cc b/osscan2.cc index 28555c8a0..812a669c9 100644 --- a/osscan2.cc +++ b/osscan2.cc @@ -640,6 +640,7 @@ HostOsScanStats::~HostOsScanStats() { void HostOsScanStats::initScanStats() { Port *tport = NULL; + Port port; int i; /* Lets find an open port to use if we don't already have one */ @@ -650,11 +651,11 @@ void HostOsScanStats::initScanStats() { if (target->FPR->osscan_opentcpport > 0) openTCPPort = target->FPR->osscan_opentcpport; - else if ((tport = target->ports.nextPort(NULL, IPPROTO_TCP, PORT_OPEN))) { + else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_TCP, PORT_OPEN))) { openTCPPort = tport->portno; /* If it is zero, let's try another one if there is one ) */ if (tport->portno == 0) - if ((tport = target->ports.nextPort(tport, IPPROTO_TCP, PORT_OPEN))) + if ((tport = target->ports.nextPort(tport, &port, IPPROTO_TCP, PORT_OPEN))) openTCPPort = tport->portno; target->FPR->osscan_opentcpport = openTCPPort; @@ -663,21 +664,21 @@ void HostOsScanStats::initScanStats() { /* Now we should find a closed port */ if (target->FPR->osscan_closedtcpport > 0) closedTCPPort = target->FPR->osscan_closedtcpport; - else if ((tport = target->ports.nextPort(NULL, IPPROTO_TCP, PORT_CLOSED))) { + else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_TCP, PORT_CLOSED))) { closedTCPPort = tport->portno; /* If it is zero, let's try another one if there is one ) */ if (tport->portno == 0) - if ((tport = target->ports.nextPort(tport, IPPROTO_TCP, PORT_CLOSED))) + if ((tport = target->ports.nextPort(tport, &port, IPPROTO_TCP, PORT_CLOSED))) closedTCPPort = tport->portno; target->FPR->osscan_closedtcpport = closedTCPPort; - } else if ((tport = target->ports.nextPort(NULL, IPPROTO_TCP, PORT_UNFILTERED))) { + } else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_TCP, PORT_UNFILTERED))) { /* Well, we will settle for unfiltered */ closedTCPPort = tport->portno; /* But again we'd prefer not to have zero */ if (tport->portno == 0) - if ((tport = target->ports.nextPort(tport, IPPROTO_TCP, PORT_UNFILTERED))) + if ((tport = target->ports.nextPort(tport, &port, IPPROTO_TCP, PORT_UNFILTERED))) closedTCPPort = tport->portno; } else { /* We'll just have to pick one at random :( */ @@ -687,19 +688,19 @@ void HostOsScanStats::initScanStats() { /* Now we should find a closed udp port */ if (target->FPR->osscan_closedudpport > 0) closedUDPPort = target->FPR->osscan_closedudpport; - else if ((tport = target->ports.nextPort(NULL, IPPROTO_UDP, PORT_CLOSED))) { + else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_UDP, PORT_CLOSED))) { closedUDPPort = tport->portno; /* Not zero, if possible */ if (tport->portno == 0) - if ((tport = target->ports.nextPort(tport, IPPROTO_UDP, PORT_CLOSED))) + if ((tport = target->ports.nextPort(tport, &port, IPPROTO_UDP, PORT_CLOSED))) closedUDPPort = tport->portno; target->FPR->osscan_closedudpport = closedUDPPort; - } else if ((tport = target->ports.nextPort(NULL, IPPROTO_UDP, PORT_UNFILTERED))) { + } else if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_UDP, PORT_UNFILTERED))) { /* Well, we will settle for unfiltered */ closedUDPPort = tport->portno; /* But not zero, please */ if (tport->portno == 0) - if ((tport = target->ports.nextPort(NULL, IPPROTO_UDP, PORT_UNFILTERED))) + if ((tport = target->ports.nextPort(NULL, &port, IPPROTO_UDP, PORT_UNFILTERED))) closedUDPPort = tport->portno; } else { /* Pick one at random. Shrug. */ diff --git a/output.cc b/output.cc index 88a6b876c..ee863cb05 100644 --- a/output.cc +++ b/output.cc @@ -202,7 +202,7 @@ static char *xml_sf_convert(const char *str) { // the service name or the service fingerprint is non-null. // Returns a pointer to a buffer containing the element, // you will have to call free on it. -static char *getServiceXMLBuf(struct serviceDeductions *sd) { +static char *getServiceXMLBuf(const struct serviceDeductions *sd) { string versionxmlstring = ""; char rpcbuf[128]; char confBuf[20]; @@ -466,33 +466,6 @@ int print_iflist(void) { return 0; } -/* Fills in namebuf (as long as there is space in buflen) with the - Name nmap normal output will use to describe the port. This takes - into account to confidence level, any SSL tunneling, etc. Truncates - namebuf to 0 length if there is no room.*/ -static void getNmapServiceName(struct serviceDeductions *sd, int state, - char *namebuf, int buflen) { - const char *tunnel_prefix; - int len; - - if (sd->service_tunnel == SERVICE_TUNNEL_SSL) - tunnel_prefix = "ssl/"; - else - tunnel_prefix = ""; - - if (sd->name != NULL && strcmp(sd->name, "unknown") != 0) { - /* The port has a name and the name is not "unknown". How confident are we? */ - if (o.servicescan && state == PORT_OPEN && sd->name_confidence <= 5) - len = Snprintf(namebuf, buflen, "%s%s?", tunnel_prefix, sd->name); - else - len = Snprintf(namebuf, buflen, "%s%s", tunnel_prefix, sd->name); - } else { - len = Snprintf(namebuf, buflen, "%sunknown", tunnel_prefix); - } - if (len >= buflen || len < 0) - namebuf[0] = '\0'; -} - #ifndef NOLUA static char *formatScriptOutput(ScriptResult sr) { std::string result = std::string(), output = sr.get_output(); @@ -549,6 +522,7 @@ void printportoutput(Target * currenths, PortList * plist) { int first = 1; struct protoent *proto; Port *current; + Port port; char hostname[1200]; struct serviceDeductions sd; NmapOutputTable *Tbl = NULL; @@ -561,6 +535,7 @@ void printportoutput(Target * currenths, PortList * plist) { unsigned int rowno; int numrows; int numignoredports = plist->numIgnoredPorts(); + int numports = plist->numPorts(); vector saved_servicefps; @@ -579,7 +554,7 @@ void printportoutput(Target * currenths, PortList * plist) { prevstate = istate; } - if (numignoredports == plist->numports) { + if (numignoredports == numports) { if (numignoredports == 0) { log_write(LOG_PLAIN, "0 ports scanned on %s\n", currenths->NameIP(hostname, sizeof(hostname))); @@ -666,7 +641,7 @@ void printportoutput(Target * currenths, PortList * plist) { if (o.servicescan || o.rpcscan) versioncol = colno++; - numrows = plist->numports - numignoredports; + numrows = numports - numignoredports; #ifndef NOLUA int scriptrows = 0; @@ -697,7 +672,7 @@ void printportoutput(Target * currenths, PortList * plist) { rowno = 1; if (o.ipprotscan) { current = NULL; - while ((current = plist->nextPort(current, IPPROTO_IP, 0)) != NULL) { + while ((current = plist->nextPort(current, &port, IPPROTO_IP, 0)) != NULL) { if (!plist->isIgnoredState(current->state)) { if (!first) log_write(LOG_MACHINE, ", "); @@ -730,8 +705,10 @@ void printportoutput(Target * currenths, PortList * plist) { } } } else { + char fullversion[160]; + current = NULL; - while ((current = plist->nextPort(current, TCPANDUDPANDSCTP, 0)) != NULL) { + while ((current = plist->nextPort(current, &port, TCPANDUDPANDSCTP, 0)) != NULL) { if (!plist->isIgnoredState(current->state)) { if (!first) log_write(LOG_MACHINE, ", "); @@ -740,7 +717,7 @@ void printportoutput(Target * currenths, PortList * plist) { strcpy(protocol, IPPROTO2STR(current->proto)); Snprintf(portinfo, sizeof(portinfo), "%d/%s", current->portno, protocol); state = statenum2str(current->state); - current->getServiceDeductions(&sd); + plist->getServiceDeductions(current->portno, current->proto, &sd); if (sd.service_fp && saved_servicefps.size() <= 8) saved_servicefps.push_back(sd.service_fp); @@ -783,7 +760,7 @@ void printportoutput(Target * currenths, PortList * plist) { (sd.name) ? sd.name : ((*rpcinfo) ? "" : "unknown"), (sd.name) ? " " : "", rpcinfo); } else { - getNmapServiceName(&sd, current->state, serviceinfo, sizeof(serviceinfo)); + current->getNmapServiceName(serviceinfo, sizeof(serviceinfo)); rpcmachineinfo[0] = '\0'; } Tbl->addItem(rowno, portcol, true, portinfo); @@ -792,8 +769,9 @@ void printportoutput(Target * currenths, PortList * plist) { if (o.reason) Tbl->addItem(rowno, reasoncol, true, port_reason_str(current->reason)); - if (*sd.fullversion) - Tbl->addItem(rowno, versioncol, true, sd.fullversion); + sd.populateFullVersionString(fullversion, sizeof(fullversion)); + if (*fullversion) + Tbl->addItem(rowno, versioncol, true, fullversion); // How should we escape illegal chars in grepable output? // Well, a reasonably clean way would be backslash escapes @@ -801,7 +779,7 @@ void printportoutput(Target * currenths, PortList * plist) { // out fields with awk, cut, and such. So I'm gonna use the // ugly hat (fitting to grepable output) or replacing the '/' // character with '|' in the version field. - Strncpy(grepvers, sd.fullversion, sizeof(grepvers) / sizeof(*grepvers)); + Strncpy(grepvers, fullversion, sizeof(grepvers) / sizeof(*grepvers)); p = grepvers; while ((p = strchr(p, '/'))) { *p = '|'; @@ -840,7 +818,7 @@ void printportoutput(Target * currenths, PortList * plist) { rowno++; #ifndef NOLUA if (o.script) { - ScriptResults::iterator ssr_iter; + ScriptResults::const_iterator ssr_iter; for (ssr_iter = current->scriptResults.begin(); ssr_iter != current->scriptResults.end(); ssr_iter++) { @@ -1907,6 +1885,7 @@ static int hostcmp(const char *a, const char *b) { scan (if it was performed) */ void printserviceinfooutput(Target * currenths) { Port *p = NULL; + Port port; struct serviceDeductions sd; int i, numhostnames = 0, numostypes = 0, numdevicetypes = 0; char hostname_tbl[MAX_SERVICE_INFO_FIELDS][MAXHOSTNAMELEN]; @@ -1917,12 +1896,12 @@ void printserviceinfooutput(Target * currenths) { for (i = 0; i < MAX_SERVICE_INFO_FIELDS; i++) hostname_tbl[i][0] = ostype_tbl[i][0] = devicetype_tbl[i][0] = '\0'; - while ((p = currenths->ports.nextPort(p, TCPANDUDPANDSCTP, PORT_OPEN))) { + while ((p = currenths->ports.nextPort(p, &port, TCPANDUDPANDSCTP, PORT_OPEN))) { // The following 2 lines (from portlist.h) tell us that we don't need to // worry about free()ing anything in the serviceDeductions struct. pass in // an allocated struct serviceDeductions (don't wory about initializing, and // you don't have to free any internal ptrs. - p->getServiceDeductions(&sd); + currenths->ports.getServiceDeductions(p->portno, p->proto, &sd); if (sd.hostname && !hostcmp(currenths->HostName(), sd.hostname)) { for (i = 0; i < MAX_SERVICE_INFO_FIELDS; i++) { diff --git a/portlist.cc b/portlist.cc index 5872a5502..59fd046eb 100644 --- a/portlist.cc +++ b/portlist.cc @@ -109,42 +109,95 @@ extern NmapOps o; /* option structure */ Port::Port() { portno = proto = 0; - rpc_status = RPC_STATUS_UNTESTED; - rpc_program = rpc_lowver = rpc_highver = 0; state = 0; - serviceprobe_results = PROBESTATE_INITIAL; - serviceprobe_service = NULL; - serviceprobe_product = serviceprobe_version = serviceprobe_extrainfo = NULL; - serviceprobe_hostname = serviceprobe_ostype = serviceprobe_devicetype = NULL; - serviceprobe_tunnel = SERVICE_TUNNEL_NONE; - serviceprobe_fp = NULL; + service = NULL; state_reason_init(&reason); } Port::~Port() { - if (serviceprobe_product) - free(serviceprobe_product); - if (serviceprobe_version) - free(serviceprobe_version); - if (serviceprobe_extrainfo) - free(serviceprobe_extrainfo); - if (serviceprobe_hostname) - free(serviceprobe_hostname); - if (serviceprobe_ostype) - free(serviceprobe_ostype); - if (serviceprobe_devicetype) - free(serviceprobe_devicetype); - if (serviceprobe_service) - free(serviceprobe_service); - if (serviceprobe_fp) - free(serviceprobe_fp); + if (service != NULL) { + if (service->name) + free(service->name); + if (service->product) + free(service->product); + if (service->version) + free(service->version); + if (service->extrainfo) + free(service->extrainfo); + if (service->hostname) + free(service->hostname); + if (service->ostype) + free(service->ostype); + if (service->devicetype) + free(service->devicetype); + if (service->service_fp) + free(service->service_fp); + delete service; + } +} + +/* Fills in namebuf (as long as there is space in buflen) with the + Name nmap normal output will use to describe the port. This takes + into account to confidence level, any SSL tunneling, etc. Truncates + namebuf to 0 length if there is no room.*/ +void Port::getNmapServiceName(char *namebuf, int buflen) const { + const char *tunnel_prefix; + const char *service_name; + int len; + + if (service != NULL && service->service_tunnel == SERVICE_TUNNEL_SSL) + tunnel_prefix = "ssl/"; + else + tunnel_prefix = ""; + + if (service != NULL && service->name != NULL) { + service_name = service->name; + } else { + struct servent *service; + + service = nmap_getservbyport(htons(portno), IPPROTO2STR(proto)); + if (service != NULL) + service_name = service->s_name; + else + service_name = NULL; + } + + if (service_name != NULL && strcmp(service_name, "unknown") != 0) { + /* The port has a name and the name is not "unknown". How confident are we? */ + if (o.servicescan && state == PORT_OPEN && (service == NULL || service->name_confidence <= 5)) + len = Snprintf(namebuf, buflen, "%s%s?", tunnel_prefix, service_name); + else + len = Snprintf(namebuf, buflen, "%s%s", tunnel_prefix, service_name); + } else { + len = Snprintf(namebuf, buflen, "%sunknown", tunnel_prefix); + } + if (len >= buflen || len < 0) + namebuf[0] = '\0'; +} + +serviceDeductions::serviceDeductions() { + name = NULL; + name_confidence = 0; + product = NULL; + version = NULL; + extrainfo = NULL; + hostname = NULL; + ostype = NULL; + devicetype = NULL; + service_tunnel = SERVICE_TUNNEL_NONE; + service_fp = NULL; + dtype = SERVICE_DETECTION_TABLE; + rpc_status = RPC_STATUS_UNTESTED; + rpc_program = 0; + rpc_lowver = 0; + rpc_highver = 0; } // Uses the sd->{product,version,extrainfo} if available to fill // out sd->fullversion. If unavailable, it will be set to zero length. -static void populateFullVersionString(struct serviceDeductions *sd) { - char *dst = sd->fullversion; - unsigned int spaceleft = sizeof(sd->fullversion) - 1; // Leave room for \0 +void serviceDeductions::populateFullVersionString(char *buf, size_t n) const { + char *dst = buf; + unsigned int spaceleft = n - 1; // Leave room for \0 int needpad = 0; // Do we need to pad a space between the next template? dst[0] = '\0'; @@ -156,52 +209,52 @@ static void populateFullVersionString(struct serviceDeductions *sd) { * that bit of information. */ - if (sd->product && spaceleft >= 8) { - if (spaceleft < strlen(sd->product)) { - strncat(dst, sd->product, spaceleft - 3); // Leave room for "..." + if (product && spaceleft >= 8) { + if (spaceleft < strlen(product)) { + strncat(dst, product, spaceleft - 3); // Leave room for "..." strncat(dst, "...", spaceleft); spaceleft = 0; } else { - strncat(dst, sd->product, spaceleft); - spaceleft -= strlen(sd->product); + strncat(dst, product, spaceleft); + spaceleft -= strlen(product); } needpad = 1; } - if (sd->version && spaceleft >= 8) { + if (version && spaceleft >= 8) { if (needpad) { strncat(dst, " ", spaceleft); spaceleft--; } - if (spaceleft < strlen(sd->version)) { - strncat(dst, sd->version, spaceleft - 3); + if (spaceleft < strlen(version)) { + strncat(dst, version, spaceleft - 3); strncat(dst, "...", spaceleft); spaceleft = 0; } else { - strncat(dst, sd->version, spaceleft); - spaceleft -= strlen(sd->version); + strncat(dst, version, spaceleft); + spaceleft -= strlen(version); } needpad = 1; } - if (sd->extrainfo && spaceleft >= 8) { + if (extrainfo && spaceleft >= 8) { if (needpad) { strncat(dst, " ", spaceleft); spaceleft--; } // This time we need to trucate inside of the () so we have spaceleft - 2 strncat(dst, "(", spaceleft); - if (spaceleft - 2 < strlen(sd->extrainfo)) { - strncat(dst, sd->extrainfo, spaceleft - 5); + if (spaceleft - 2 < strlen(extrainfo)) { + strncat(dst, extrainfo, spaceleft - 5); strncat(dst, "...", spaceleft - 2); spaceleft = 1; // Fit the paren } else { - strncat(dst, sd->extrainfo, spaceleft); - spaceleft -= (strlen(sd->extrainfo) + 2); + strncat(dst, extrainfo, spaceleft); + spaceleft -= (strlen(extrainfo) + 2); } strncat(dst, ")", spaceleft); spaceleft--; @@ -214,74 +267,24 @@ static void populateFullVersionString(struct serviceDeductions *sd) { // initializing, and you don't have to free any internal ptrs. See the // serviceDeductions definition for the fields that are populated. // Returns 0 if at least a name is available. -int Port::getServiceDeductions(struct serviceDeductions *sd) { - struct servent *service; +const void PortList::getServiceDeductions(u16 portno, int protocol, struct serviceDeductions *sd) const { + const Port *port; - assert(sd); - memset(sd, 0, sizeof(struct serviceDeductions)); - sd->service_fp = serviceprobe_fp; - sd->service_tunnel = serviceprobe_tunnel; - sd->rpc_status = rpc_status; - sd->rpc_program = rpc_program; - sd->rpc_lowver = rpc_lowver; - sd->rpc_highver = rpc_highver; + port = lookupPort(portno, protocol); + if (port == NULL || port->service == NULL) { + struct servent *service; - // First priority is RPC - if (rpc_status == RPC_STATUS_UNKNOWN || rpc_status == RPC_STATUS_GOOD_PROG ) { - assert(serviceprobe_service); - sd->name = serviceprobe_service; - sd->name_confidence = (rpc_status == RPC_STATUS_UNKNOWN)? 8 : 10; - sd->dtype = SERVICE_DETECTION_PROBED; // RPC counts as probed - sd->version = serviceprobe_version; - sd->extrainfo = serviceprobe_extrainfo; - sd->hostname = serviceprobe_hostname; - sd->ostype = serviceprobe_ostype; - sd->devicetype = serviceprobe_devicetype; - populateFullVersionString(sd); - return 0; - } else if (serviceprobe_results == PROBESTATE_FINISHED_HARDMATCHED - || serviceprobe_results == PROBESTATE_FINISHED_SOFTMATCHED) { - assert(serviceprobe_service); - sd->dtype = SERVICE_DETECTION_PROBED; - sd->name = serviceprobe_service; - sd->name_confidence = 10; - sd->product = serviceprobe_product; - sd->version = serviceprobe_version; - sd->extrainfo = serviceprobe_extrainfo; - sd->hostname = serviceprobe_hostname; - sd->ostype = serviceprobe_ostype; - sd->devicetype = serviceprobe_devicetype; - populateFullVersionString(sd); - return 0; - } else if (serviceprobe_results == PROBESTATE_EXCLUDED) { - service = nmap_getservbyport(htons(portno), IPPROTO2STR(proto)); - - if (service) sd->name = service->s_name; - - sd->name_confidence = 2; // Since we didn't even check it, we aren't very confident - sd->dtype = SERVICE_DETECTION_TABLE; - sd->product = serviceprobe_product; // Should have a string that says port was excluded - populateFullVersionString(sd); - return 0; - } else if (serviceprobe_results == PROBESTATE_FINISHED_TCPWRAPPED) { - sd->dtype = SERVICE_DETECTION_PROBED; - sd->name = "tcpwrapped"; - sd->name_confidence = 8; - return 0; - } - - // So much for service detection or RPC. Maybe we can find it in the file - service = nmap_getservbyport(htons(portno), IPPROTO2STR(proto)); - if (service) { - sd->dtype = SERVICE_DETECTION_TABLE; - sd->name = service->s_name; + /* Look up the service name. */ + *sd = serviceDeductions(); + service = nmap_getservbyport(htons(portno), IPPROTO2STR(protocol)); + if (service != NULL) + sd->name = service->s_name; + else + sd->name = NULL; sd->name_confidence = 3; - return 0; + } else { + *sd = *port->service; } - - // Couldn't find it. [shrug] - return -1; - } @@ -312,72 +315,112 @@ static char *cstringSanityCheck(const char* string, int len) { return result; } -void Port::setServiceProbeResults(enum serviceprobestate sres, - const char *sname, - enum service_tunnel_type tunnel, - const char *product, const char *version, - const char *extrainfo, const char *hostname, - const char *ostype, const char *devicetype, - const char *fingerprint) { +void PortList::setServiceProbeResults(u16 portno, int protocol, + enum serviceprobestate sres, const char *sname, + enum service_tunnel_type tunnel, const char *product, const char *version, + const char *extrainfo, const char *hostname, const char *ostype, + const char *devicetype, const char *fingerprint) { + Port *port; - serviceprobe_results = sres; - serviceprobe_tunnel = tunnel; + port = createPort(portno, protocol); + if (port->service == NULL) + port->service = new serviceDeductions; - if (sname) - serviceprobe_service = strdup(sname); - else - serviceprobe_service = NULL; + if (sres == PROBESTATE_FINISHED_HARDMATCHED + || sres == PROBESTATE_FINISHED_SOFTMATCHED) { + port->service->dtype = SERVICE_DETECTION_PROBED; + port->service->name_confidence = 10; + } else if (sres == PROBESTATE_EXCLUDED) { + if (sname == NULL) { + struct servent *service; + service = nmap_getservbyport(htons(portno), IPPROTO2STR(protocol)); + if (service != NULL) + sname = service->s_name; + } + port->service->dtype = SERVICE_DETECTION_TABLE; + port->service->name_confidence = 2; // Since we didn't even check it, we aren't very confident + } else if (sres == PROBESTATE_FINISHED_TCPWRAPPED) { + port->service->dtype = SERVICE_DETECTION_PROBED; + if (sname == NULL) + sname = "tcpwrapped"; + port->service->dtype = SERVICE_DETECTION_TABLE; + port->service->name_confidence = 8; + } - if (fingerprint) - serviceprobe_fp = strdup(fingerprint); - else - serviceprobe_fp = NULL; + // port->serviceprobe_results = sres; + port->service->service_tunnel = tunnel; - serviceprobe_product = cstringSanityCheck(product, 80); - serviceprobe_version = cstringSanityCheck(version, 80); - serviceprobe_extrainfo = cstringSanityCheck(extrainfo, 256); - serviceprobe_hostname = cstringSanityCheck(hostname, 80); - serviceprobe_ostype = cstringSanityCheck(ostype, 32); - serviceprobe_devicetype = cstringSanityCheck(devicetype, 32); + if (sname) + port->service->name = strdup(sname); + else + port->service->name = NULL; + + if (fingerprint) + port->service->service_fp = strdup(fingerprint); + else + port->service->service_fp = NULL; + + port->service->product = cstringSanityCheck(product, 80); + port->service->version = cstringSanityCheck(version, 80); + port->service->extrainfo = cstringSanityCheck(extrainfo, 256); + port->service->hostname = cstringSanityCheck(hostname, 80); + port->service->ostype = cstringSanityCheck(ostype, 32); + port->service->devicetype = cstringSanityCheck(devicetype, 32); } /* Sets the results of an RPC scan. if rpc_status is not RPC_STATUS_GOOD_PROGRAM, pass 0 for the other args. This function takes care of setting the port's service and version appropriately. */ -void Port::setRPCProbeResults(int rpcs, unsigned long rpcp, +void PortList::setRPCProbeResults(u16 portno, int proto, int rpcs, unsigned long rpcp, unsigned int rpcl, unsigned int rpch) { - rpc_status = rpcs; + Port *port; const char *newsvc; char verbuf[128]; - rpc_status = rpcs; - if (rpc_status == RPC_STATUS_GOOD_PROG) { - rpc_program = rpcp; - rpc_lowver = rpcl; - rpc_highver = rpch; + port = createPort(portno, proto); + if (port->service == NULL) + port->service = new serviceDeductions; + + port->service->rpc_status = rpcs; + if (port->service->rpc_status == RPC_STATUS_GOOD_PROG) { + port->service->rpc_program = rpcp; + port->service->rpc_lowver = rpcl; + port->service->rpc_highver = rpch; // Now set the service/version info newsvc = nmap_getrpcnamebynum(rpcp); if (!newsvc) newsvc = "rpc.unknownprog"; // should never happen - if (serviceprobe_service) - free(serviceprobe_service); - serviceprobe_service = strdup(newsvc); - serviceprobe_product = strdup(newsvc); - if (rpc_lowver == rpc_highver) - Snprintf(verbuf, sizeof(verbuf), "%i", rpc_lowver); + if (port->service->name) + free(port->service->name); + port->service->name = strdup(newsvc); + if (port->service->rpc_lowver == port->service->rpc_highver) + Snprintf(verbuf, sizeof(verbuf), "%i", port->service->rpc_lowver); else - Snprintf(verbuf, sizeof(verbuf), "%i-%i", rpc_lowver, rpc_highver); - serviceprobe_version = strdup(verbuf); - Snprintf(verbuf, sizeof(verbuf), "rpc #%li", rpc_program); - serviceprobe_extrainfo = strdup(verbuf); - } else if (rpc_status == RPC_STATUS_UNKNOWN) { - if (serviceprobe_service) - free(serviceprobe_service); + Snprintf(verbuf, sizeof(verbuf), "%i-%i", port->service->rpc_lowver, port->service->rpc_highver); + port->service->version = strdup(verbuf); + Snprintf(verbuf, sizeof(verbuf), "rpc #%li", port->service->rpc_program); + port->service->extrainfo = strdup(verbuf); + port->service->name_confidence = 10; + port->service->dtype = SERVICE_DETECTION_PROBED; + } else if (port->service->rpc_status == RPC_STATUS_UNKNOWN) { + if (port->service->name) + free(port->service->name); - serviceprobe_service = strdup("rpc.unknown"); + port->service->name = strdup("rpc.unknown"); + port->service->name_confidence = 8; + port->service->dtype = SERVICE_DETECTION_PROBED; } } + +void PortList::addScriptResult(u16 portno, int protocol, ScriptResult& sr) { + Port *port; + + port = createPort(portno, protocol); + + port->scriptResults.push_back(sr); +} + /*****************************************************************************/ /* Convert protocol name from in.h to enum portlist_proto. * So IPPROTO_TCP will be changed to PORTLIST_PROTO_TCP and so on. */ @@ -387,6 +430,12 @@ void Port::setRPCProbeResults(int rpcs, unsigned long rpcp, (p)==IPPROTO_SCTP ? PORTLIST_PROTO_SCTP : \ PORTLIST_PROTO_IP) +#define PORTLISTPROTO2INPROTO(p) \ + ((p)==PORTLIST_PROTO_TCP ? IPPROTO_TCP : \ + (p)==PORTLIST_PROTO_UDP ? IPPROTO_UDP : \ + (p)==PORTLIST_PROTO_SCTP ? IPPROTO_SCTP : \ + IPPROTO_IP) + PortList::PortList() { int proto; @@ -396,9 +445,11 @@ PortList::PortList() { for(proto=0; proto < PORTLIST_PROTO_MAX; proto++) { if(port_list_count[proto] > 0) port_list[proto] = (Port**) safe_zalloc(sizeof(Port*)*port_list_count[proto]); + default_port_state[proto].proto = PORTLISTPROTO2INPROTO(proto); + default_port_state[proto].reason.reason_id = ER_NORESPONSE; + state_counts_proto[proto][default_port_state[proto].state] = port_list_count[proto]; } - numports = 0; numscriptresults = 0; idstr = NULL; } @@ -422,8 +473,22 @@ PortList::~PortList() { } } +void PortList::setDefaultPortState(u8 protocol, int state) { + int proto = INPROTO2PORTLISTPROTO(protocol); + int i; -int PortList::addPort(u16 portno, u8 protocol, int state) { + for (i = 0; i < port_list_count[proto]; i++) { + if (port_list[proto][i] == NULL) { + state_counts_proto[proto][default_port_state[proto].state]--; + state_counts_proto[proto][state]++; + } + } + + default_port_state[proto].state = state; +} + +void PortList::setPortState(u16 portno, u8 protocol, int state) { + const Port *oldport; Port *current; int proto = INPROTO2PORTLISTPROTO(protocol); @@ -445,55 +510,35 @@ int PortList::addPort(u16 portno, u8 protocol, int state) { assert(protocol!=IPPROTO_IP || portno<256); - current = getPortEntry(portno, protocol); - if (current) { + oldport = lookupPort(portno, protocol); + if (oldport != NULL) { /* We must discount our statistics from the old values. Also warn if a complete duplicate */ - if (o.debugging && current->state == state) { + if (o.debugging && oldport->state == state) { error("Duplicate port (%hu/%s)", portno, proto2ascii(protocol)); } - state_counts_proto[proto][current->state]--; + state_counts_proto[proto][oldport->state]--; } else { - current = new Port(); - current->portno = portno; - current->proto = protocol; - numports++; - - setPortEntry(portno, protocol, current); + state_counts_proto[proto][default_port_state[proto].state]--; } + current = createPort(portno, protocol); current->state = state; state_counts_proto[proto][state]++; if(state == PORT_FILTERED || state == PORT_OPENFILTERED) setStateReason(portno, protocol, ER_NORESPONSE, 0, 0); - return 0; /*success */ + return; } -int PortList::removePort(u16 portno, u8 protocol) { - Port *answer = NULL; +int PortList::getPortState(u16 portno, u8 protocol) { + const Port *port; - log_write(LOG_PLAIN, "Removed %d\n", portno); - - answer = getPortEntry(portno, protocol); - if (!answer) + port = lookupPort(portno, protocol); + if (port == NULL) return -1; - setPortEntry(portno, protocol, NULL); - - if (o.verbose) { - log_write(LOG_STDOUT, "Deleting port %hu/%s, which we thought was %s\n", - portno, proto2ascii(answer->proto), - statenum2str(answer->state)); - log_flush(LOG_STDOUT); - } - - /* Discount statistics */ - state_counts_proto[INPROTO2PORTLISTPROTO(protocol)][answer->state]--; - numports--; - - delete answer; - return 0; + return port->state; } /* Saves an identification string for the target containing these @@ -512,13 +557,13 @@ void PortList::setIdStr(const char *id) { int PortList::getStateCounts(int protocol, int state){ - return(state_counts_proto[INPROTO2PORTLISTPROTO(protocol)][state]); + return state_counts_proto[INPROTO2PORTLISTPROTO(protocol)][state]; } int PortList::getStateCounts(int state){ int sum=0, proto; for(proto=0; proto < PORTLIST_PROTO_MAX; proto++) - sum += state_counts_proto[proto][state]; + sum += getStateCounts(PORTLISTPROTO2INPROTO(proto), state); return(sum); } @@ -532,19 +577,19 @@ int PortList::getStateCounts(int state){ function returns ports in numeric order from lowest to highest, except that if you ask for both TCP, UDP & SCTP, every TCP port will be returned before we start returning UDP and SCTP ports */ -Port *PortList::nextPort(Port *afterthisport, +Port *PortList::nextPort(const Port *cur, Port *next, int allowed_protocol, int allowed_state) { int proto; int mapped_pno; Port *port; - if(afterthisport) { - proto = INPROTO2PORTLISTPROTO(afterthisport->proto); + if (cur) { + proto = INPROTO2PORTLISTPROTO(cur->proto); assert(port_map[proto]!=NULL); // Hmm, it's not posible to handle port that doesn't have anything in map - assert(afterthisport->proto!=IPPROTO_IP || afterthisport->portno<256); - mapped_pno = port_map[proto][afterthisport->portno]; + assert(cur->proto!=IPPROTO_IP || cur->portno<256); + mapped_pno = port_map[proto][cur->portno]; mapped_pno++; // we're interested in next port after current - }else { // running for the first time + } else { // running for the first time if (allowed_protocol == TCPANDUDPANDSCTP) proto = INPROTO2PORTLISTPROTO(IPPROTO_TCP); else if (allowed_protocol == UDPANDSCTP) @@ -557,78 +602,128 @@ Port *PortList::nextPort(Port *afterthisport, if(port_list[proto] != NULL) { for(;mapped_pno < port_list_count[proto]; mapped_pno++) { port = port_list[proto][mapped_pno]; - if(port && (allowed_state==0 || port->state==allowed_state)) - return(port); + if (port && (allowed_state==0 || port->state==allowed_state)) { + *next = *port; + return next; + } + if (!port && (allowed_state==0 || default_port_state[proto].state==allowed_state)) { + *next = default_port_state[proto]; + next->portno = port_map_rev[proto][mapped_pno]; + return next; + } } } /* if all protocols, than after TCP search UDP & SCTP */ - if((!afterthisport && allowed_protocol == TCPANDUDPANDSCTP) || - (afterthisport && proto == INPROTO2PORTLISTPROTO(IPPROTO_TCP))) - return(nextPort(NULL, UDPANDSCTP, allowed_state)); + if((!cur && allowed_protocol == TCPANDUDPANDSCTP) || + (cur && proto == INPROTO2PORTLISTPROTO(IPPROTO_TCP))) + return(nextPort(NULL, next, UDPANDSCTP, allowed_state)); /* if all protocols, than after UDP search SCTP */ - if((!afterthisport && allowed_protocol == UDPANDSCTP) || - (afterthisport && proto == INPROTO2PORTLISTPROTO(IPPROTO_UDP))) - return(nextPort(NULL, IPPROTO_SCTP, allowed_state)); + if((!cur && allowed_protocol == UDPANDSCTP) || + (cur && proto == INPROTO2PORTLISTPROTO(IPPROTO_UDP))) + return(nextPort(NULL, next, IPPROTO_SCTP, allowed_state)); return(NULL); } - -Port *PortList::getPortEntry(u16 portno, u8 protocol) { - int proto = INPROTO2PORTLISTPROTO(protocol); - int mapped_pno; - assert(protocol!=IPPROTO_IP || portno<256); - if(port_map[proto]==NULL || port_list[proto]==NULL) - fatal("%s(%i,%i): you're trying to access uninitialized protocol", __func__, portno, protocol); - mapped_pno = port_map[proto][portno]; +/* Convert portno and protocol into the internal indices used to index + port_list. Returns false on error, true otherwise. */ +bool PortList::mapPort(u16 *portno, u8 *protocol) const { + int mapped_portno, mapped_protocol; - assert(mapped_pno < port_list_count[proto]); - assert(mapped_pno >= 0); + mapped_protocol = INPROTO2PORTLISTPROTO(*protocol); + + if (*protocol == IPPROTO_IP) + assert(*portno < 256); + if(port_map[mapped_protocol]==NULL || port_list[mapped_protocol]==NULL) + fatal("%s(%i,%i): you're trying to access uninitialized protocol", __func__, *portno, *protocol); + mapped_portno = port_map[mapped_protocol][*portno]; + + assert(mapped_portno < port_list_count[mapped_protocol]); + assert(mapped_portno >= 0); - /* The ugly hack: we allow only port 0 to be mapped to 0 position */ - if(mapped_pno==0 && portno!=0) { - error("WARNING: %s(%i,%i): this port was not mapped", __func__, portno, protocol); - return(NULL); - }else - return(port_list[proto][mapped_pno]); + *portno = mapped_portno; + *protocol = mapped_protocol; + + return true; } -void PortList::setPortEntry(u16 portno, u8 protocol, Port *port) { - int proto = INPROTO2PORTLISTPROTO(protocol); - int mapped_pno; +const Port *PortList::lookupPort(u16 portno, u8 protocol) const { + if (!mapPort(&portno, &protocol)) + return NULL; - assert(protocol!=IPPROTO_IP || portno<256); - if(port_map[proto]==NULL || port_list[proto]==NULL) - fatal("%s(%i,%i): you're trying to access uninitialized protocol", __func__, portno, protocol); - mapped_pno = port_map[proto][portno]; + return port_list[protocol][portno]; +} - assert(mapped_pno < port_list_count[proto]); - assert(mapped_pno >= 0); - - /* The ugly hack: we allow only port 0 to be mapped to 0 position */ - if(mapped_pno==0 && portno!=0) { - error("WARNING: %s(%i,%i): this port was not mapped", __func__, portno, protocol); - return; +/* Create the port if it doesn't exist; otherwise this is like lookupPort. */ +Port *PortList::createPort(u16 portno, u8 protocol) { + u16 mapped_portno; + u8 mapped_protocol; + + mapped_portno = portno; + mapped_protocol = protocol; + if (!mapPort(&mapped_portno, &mapped_protocol)) + return NULL; + + if (port_list[mapped_protocol][mapped_portno] == NULL) { + Port *p = new Port(); + p->portno = portno; + p->proto = protocol; + port_list[mapped_protocol][mapped_portno] = p; } - - port_list[proto][mapped_pno] = port; + + return port_list[mapped_protocol][mapped_portno]; +} + +int PortList::forgetPort(u16 portno, u8 protocol) { + Port *answer = NULL; + + log_write(LOG_PLAIN, "Removed %d\n", portno); + + if (!mapPort(&portno, &protocol)) + return -1; + + answer = port_list[protocol][portno]; + if (answer == NULL) + return -1; + + state_counts_proto[protocol][answer->state]--; + state_counts_proto[protocol][default_port_state[protocol].state]++; + + delete answer; + + port_list[protocol][portno] = NULL; + + if (o.verbose) { + log_write(LOG_STDOUT, "Deleting port %hu/%s, which we thought was %s\n", + portno, proto2ascii(answer->proto), + statenum2str(answer->state)); + log_flush(LOG_STDOUT); + } + + return 0; } /* Just free memory used by PortList::port_map[]. Should be done somewhere * before closing nmap. */ void PortList::freePortMap(){ int proto; - for(proto=0; proto < PORTLIST_PROTO_MAX; proto++) + for(proto=0; proto < PORTLIST_PROTO_MAX; proto++) { if(port_map[proto]){ free(port_map[proto]); port_map[proto] = NULL; } + if (port_map_rev[proto]) { + free(port_map_rev[proto]); + port_map_rev[proto] = NULL; + } + } } u16 *PortList::port_map[PORTLIST_PROTO_MAX]; +u16 *PortList::port_map_rev[PORTLIST_PROTO_MAX]; int PortList::port_list_count[PORTLIST_PROTO_MAX]; /* This function must be runned before any PortList object is created. @@ -636,38 +731,27 @@ int PortList::port_list_count[PORTLIST_PROTO_MAX]; * should be sorted. */ void PortList::initializePortMap(int protocol, u16 *ports, int portcount) { int i; - int unused_zero; // aren't we using 0 port? int ports_max = (protocol == IPPROTO_IP) ? 256 : 65536; int proto = INPROTO2PORTLISTPROTO(protocol); - if(port_map[proto]!=NULL) + if (port_map[proto] != NULL || port_map_rev[proto] != NULL) fatal("%s: portmap for protocol %i already initialized", __func__, protocol); assert(port_list_count[proto]==0); /* this memory will never be freed, but this is the way it has to be. */ - port_map[proto] = (u16*) safe_zalloc(sizeof(u16)*ports_max); + port_map[proto] = (u16 *) safe_zalloc(sizeof(u16) * ports_max); + port_map_rev[proto] = (u16 *) safe_zalloc(sizeof(u16) * portcount); - /* Is zero port to be unused? */ - if(portcount==0 || ports[0]!=0) - unused_zero = 1; - else - unused_zero = 0; - - /* The ugly hack: if we don't use 0 port, than we need one more extra element. */ - port_list_count[proto] = portcount + unused_zero; + port_list_count[proto] = portcount; for(i=0; i < portcount; i++) { - /* The ugly hack: if we don't use 0 port, than we must start counting from 1 */ - port_map[proto][ports[i]] = i + unused_zero; // yes, this is the key line + port_map[proto][ports[i]] = i; + port_map_rev[proto][i] = ports[i]; } /* So now port_map should have such structure (lets scan 2nd,4th and 6th port): * port_map[0,0,1,0,2,0,3,...] <- indexes to port_list structure - * port_list[0,port_2,port_4,port_6] - * But if we scan 0, 2, and 4 port: - * port_map[0,0,1,0,2,...] // yes, this 0 in first place isn't mistake - * port_list[port_0,port_2,port_4] - * And in both cases we scan three ports. Ugly, isn't it? :) */ + * port_list[port_2,port_4,port_6] */ } /* Cycles through the 0 or more "ignored" ports which should be @@ -761,20 +845,26 @@ int PortList::numIgnoredPorts() { return numports; } +int PortList::numPorts() const { + int proto, num = 0; + + for (proto = 0; proto < PORTLIST_PROTO_MAX; proto++) + num += port_list_count[proto]; + + return num; +} + int PortList::setStateReason(u16 portno, u8 proto, reason_t reason, u8 ttl, u32 ip_addr) { Port *answer = NULL; - if(!(answer = getPortEntry(portno, proto))) - return -1; if(reason > ER_MAX) return -1; + answer = createPort(portno, proto); /* set new reason and increment its count */ answer->reason.reason_id = reason; answer->reason.ip_addr.s_addr = ip_addr; answer->reason.ttl = ttl; - answer->reason.state = answer->state; - setPortEntry(portno, proto, answer); return 0; } diff --git a/portlist.h b/portlist.h index 42b6c5ab2..af2150537 100644 --- a/portlist.h +++ b/portlist.h @@ -140,25 +140,25 @@ enum service_tunnel_type { SERVICE_TUNNEL_NONE, SERVICE_TUNNEL_SSL }; void random_port_cheat(u16 *ports, int portcount); struct serviceDeductions { - const char *name; // will be NULL if can't determine + serviceDeductions(); + void populateFullVersionString(char *buf, size_t n) const; + + char *name; // will be NULL if can't determine // Confidence is a number from 0 (least confident) to 10 (most // confident) expressing how accurate the service detection is // likely to be. int name_confidence; // Any of these 6 can be NULL if we weren't able to determine it - const char *product; - const char *version; - const char *extrainfo; - const char *hostname; - const char *ostype; - const char *devicetype; + char *product; + char *version; + char *extrainfo; + char *hostname; + char *ostype; + char *devicetype; // SERVICE_TUNNEL_NONE or SERVICE_TUNNEL_SSL enum service_tunnel_type service_tunnel; - // This is a combined representation of product, version, and extrainfo. - // It will be zero length if unavailable. - char fullversion[160]; // if we should give the user a service fingerprint to submit, here it is. Otherwise NULL. - const char *service_fp; + char *service_fp; enum service_detection_type dtype; // definition above int rpc_status; /* RPC_STATUS_UNTESTED means we haven't checked RPC_STATUS_UNKNOWN means the port appears to be RPC @@ -173,38 +173,12 @@ struct serviceDeductions { }; class Port { + friend class PortList; + public: Port(); ~Port(); - - // pass in an allocated struct serviceDeductions (don't worry about initializing, and - // you don't have to free any internal ptrs. See the serviceDeductions definition for - // the fields that are populated. Returns 0 if at least a name is available. - int getServiceDeductions(struct serviceDeductions *sd); - - // sname should be NULL if sres is not - // PROBESTATE_FINISHED_MATCHED. product,version, and/or extrainfo - // will be NULL if unavailable. Note that this function makes its - // own copy of sname and product/version/extrainfo. This function - // also takes care of truncating the version strings to a - // 'reasonable' length if neccessary, and cleaning up any unprinable - // chars. (these tests are to avoid annoying DOS (or other) attacks - // by malicious services). The fingerprint should be NULL unless - // one is available and the user should submit it. tunnel must be - // SERVICE_TUNNEL_NULL (normal) or SERVICE_TUNNEL_SSL (means ssl was - // detected and we tried to tunnel through it ). - void setServiceProbeResults(enum serviceprobestate sres, const char *sname, - enum service_tunnel_type tunnel, const char *product, - const char *version, const char *hostname, - const char *ostype, const char *devicetype, - const char *extrainfo, const char *fingerprint); - - /* Sets the results of an RPC scan. if rpc_status is not - RPC_STATUS_GOOD_PROGRAM, pass 0 for the other args. This function - takes care of setting the port's service and version - appropriately. */ - void setRPCProbeResults(int rpc_status, unsigned long rpc_program, - unsigned int rpc_lowver, unsigned int rpc_highver); + void getNmapServiceName(char *namebuf, int buflen) const; u16 portno; u8 proto; @@ -216,28 +190,10 @@ class Port { #endif private: - int rpc_status; /* RPC_STATUS_UNTESTED means we haven't checked - RPC_STATUS_UNKNOWN means the port appears to be RPC - but we couldn't find a match - RPC_STATUS_GOOD_PROG means rpc_program gives the prog # - RPC_STATUS_NOT_RPC means the port doesn't appear to - be RPC */ - unsigned long rpc_program; /* Only valid if rpc_state == RPC_STATUS_GOOD_PROG */ - unsigned int rpc_lowver; - unsigned int rpc_highver; - enum serviceprobestate serviceprobe_results; // overall results of service scan - char *serviceprobe_service; // If a service was discovered, points to the name - // Any of these next three can be NULL if the details are not available - char *serviceprobe_product; - char *serviceprobe_version; - char *serviceprobe_extrainfo; - char *serviceprobe_hostname; - char *serviceprobe_ostype; - char *serviceprobe_devicetype; - enum service_tunnel_type serviceprobe_tunnel; - // A fingerprint that the user can submit if the service wasn't recognized - char *serviceprobe_fp; - + /* This is allocated only on demand by PortList::setServiceProbeResults or + PortList::setRPCProbeResults, to save memory for the many closed or + filtered ports that don't need it. */ + serviceDeductions *service; }; @@ -261,10 +217,10 @@ class PortList { /* Free memory used by port_map. It should be done somewhere before quitting*/ static void freePortMap(); - /* Add a new port to this list. If the state has changed, it is - OK to call this function to effect the change */ - int addPort(u16 portno, u8 protocol, int state); - int removePort(u16 portno, u8 protocol); + void setDefaultPortState(u8 protocol, int state); + void setPortState(u16 portno, u8 protocol, int state); + int getPortState(u16 portno, u8 protocol); + int forgetPort(u16 portno, u8 protocol); /* Saves an identification string for the target containing these ports (an IP addrss might be a good example, but set what you want). Only used when printing new port updates. Optional. A @@ -280,17 +236,11 @@ class PortList { function returns ports in numeric order from lowest to highest, except that if you ask for TCP, UDP & SCTP, all TCP ports will be returned before we start returning UDP and finally SCTP ports */ - Port *nextPort(Port *afterthisport, - int allowed_protocol, int allowed_state); - - /* Get Port structure from PortList structure.*/ - Port *getPortEntry(u16 portno, u8 protocol); - /* Set Port structure to PortList structure.*/ - void setPortEntry(u16 portno, u8 protocol, Port *port); + Port *nextPort(const Port *cur, Port *next, + int allowed_protocol, int allowed_state); int setStateReason(u16 portno, u8 proto, reason_t reason, u8 ttl, u32 ip_addr); - int numports; /* Total number of ports in list in ANY state */ int numscriptresults; /* Total number of scripts which produced output */ /* Get number of ports in this state. This a sum for protocols. */ @@ -298,6 +248,38 @@ class PortList { /* Get number of ports in this state for requested protocol. */ int getStateCounts(int protocol, int state); + // sname should be NULL if sres is not + // PROBESTATE_FINISHED_MATCHED. product,version, and/or extrainfo + // will be NULL if unavailable. Note that this function makes its + // own copy of sname and product/version/extrainfo. This function + // also takes care of truncating the version strings to a + // 'reasonable' length if neccessary, and cleaning up any unprinable + // chars. (these tests are to avoid annoying DOS (or other) attacks + // by malicious services). The fingerprint should be NULL unless + // one is available and the user should submit it. tunnel must be + // SERVICE_TUNNEL_NULL (normal) or SERVICE_TUNNEL_SSL (means ssl was + // detected and we tried to tunnel through it ). + void setServiceProbeResults(u16 portno, int protocol, + enum serviceprobestate sres, const char *sname, + enum service_tunnel_type tunnel, const char *product, + const char *version, const char *hostname, + const char *ostype, const char *devicetype, + const char *extrainfo, const char *fingerprint); + + // pass in an allocated struct serviceDeductions (don't worry about initializing, and + // you don't have to free any internal ptrs. See the serviceDeductions definition for + // the fields that are populated. Returns 0 if at least a name is available. + const void getServiceDeductions(u16 portno, int protocol, struct serviceDeductions *sd) const; + + /* Sets the results of an RPC scan. if rpc_status is not + RPC_STATUS_GOOD_PROGRAM, pass 0 for the other args. This function + takes care of setting the port's service and version + appropriately. */ + void setRPCProbeResults(u16 portno, int proto, int rpc_status, unsigned long rpc_program, + unsigned int rpc_lowver, unsigned int rpc_highver); + + void addScriptResult(u16 portno, int protocol, ScriptResult& sr); + /* Cycles through the 0 or more "ignored" ports which should be consolidated for Nmap output. They are returned sorted by the number of prots in the state, starting with the most common. It @@ -313,12 +295,20 @@ class PortList { int numIgnoredStates(); int numIgnoredPorts(); + int numPorts() const; private: + bool mapPort(u16 *portno, u8 *protocol) const; + /* Get Port structure from PortList structure.*/ + const Port *lookupPort(u16 portno, u8 protocol) const; + Port *createPort(u16 portno, u8 protocol); + /* Set Port structure to PortList structure.*/ + void setPortEntry(u16 portno, u8 protocol, Port *port); + /* A string identifying the system these ports are on. Just used for printing open ports, if it is set with setIdStr() */ char *idstr; - /* Number of ports in each state per each protocol */ + /* Number of ports in each state per each protocol. */ int state_counts_proto[PORTLIST_PROTO_MAX][PORT_HIGHEST_STATE]; Port **port_list[PORTLIST_PROTO_MAX]; protected: @@ -326,8 +316,10 @@ class PortList { * Only functions: getPortEntry, setPortEntry, initializePortMap and * nextPort should access this structure directly. */ static u16 *port_map[PORTLIST_PROTO_MAX]; + static u16 *port_map_rev[PORTLIST_PROTO_MAX]; /* Number of allocated elements in port_list per each protocol. */ static int port_list_count[PORTLIST_PROTO_MAX]; + Port default_port_state[PORTLIST_PROTO_MAX]; }; #endif diff --git a/portreasons.cc b/portreasons.cc index 496f2c893..720abb78e 100644 --- a/portreasons.cc +++ b/portreasons.cc @@ -246,6 +246,7 @@ static int update_state_summary(state_reason_summary_t *head, reason_t reason_id * state_reason_summary structures using update_state_summary */ static unsigned int get_state_summary(state_reason_summary_t *head, PortList *Ports, int state) { Port *current = NULL; + Port port; state_reason_summary_t *reason; unsigned int total = 0; unsigned short proto = (o.ipprotscan) ? IPPROTO_IP : TCPANDUDPANDSCTP; @@ -254,7 +255,7 @@ static unsigned int get_state_summary(state_reason_summary_t *head, PortList *Po return 0; reason = head; - while((current = Ports->nextPort(current, proto, state)) != NULL) { + while((current = Ports->nextPort(current, &port, proto, state)) != NULL) { if(Ports->isIgnoredState(current->state)) { total++; update_state_summary(reason, current->reason.reason_id); diff --git a/portreasons.h b/portreasons.h index 0aef1f0ef..f0a87d970 100644 --- a/portreasons.h +++ b/portreasons.h @@ -114,7 +114,6 @@ typedef struct port_reason { reason_t reason_id; struct in_addr ip_addr; unsigned short ttl; - int state; } state_reason_t; /* used to calculate state reason summaries. diff --git a/scan_engine.cc b/scan_engine.cc index 12998c8d0..acd0f78bd 100644 --- a/scan_engine.cc +++ b/scan_engine.cc @@ -1103,33 +1103,6 @@ static bool pingprobe_is_appropriate(const UltraScanInfo *USI, return false; } -/* For the given scan type, this returns the port/host state demonstrated - by getting no response back */ -static int scantype_no_response_means(stype scantype) { - switch(scantype) { - case SYN_SCAN: - case ACK_SCAN: - case WINDOW_SCAN: - case CONNECT_SCAN: - case SCTP_INIT_SCAN: - return PORT_FILTERED; - case UDP_SCAN: - case IPPROT_SCAN: - case NULL_SCAN: - case FIN_SCAN: - case MAIMON_SCAN: - case XMAS_SCAN: - case SCTP_COOKIE_ECHO_SCAN: - return PORT_OPENFILTERED; - case PING_SCAN: - case PING_SCAN_ARP: - return HOST_DOWN; - default: - fatal("Unexpected scan type found in %s()", __func__); - } - return 0; /* Unreached */ -} - HostScanStats::HostScanStats(Target *t, UltraScanInfo *UltraSI) { target = t; USI=UltraSI; @@ -1497,6 +1470,46 @@ static void init_perf_values(struct ultra_scan_performance_vars *perf) { perf->tryno_cap = o.getMaxRetransmissions(); } +/* Initialize the state for ports that don't receive a response in all the + targets. */ +static void set_default_port_state(vector &targets, stype scantype) { + vector::iterator target; + + for (target = targets.begin(); target != targets.end(); target++) { + switch (scantype) { + case SYN_SCAN: + case ACK_SCAN: + case WINDOW_SCAN: + case CONNECT_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_TCP, PORT_FILTERED); + break; + case SCTP_INIT_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_SCTP, PORT_FILTERED); + break; + case NULL_SCAN: + case FIN_SCAN: + case MAIMON_SCAN: + case XMAS_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_TCP, PORT_OPENFILTERED); + break; + case UDP_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_UDP, PORT_OPENFILTERED); + break; + case IPPROT_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_IP, PORT_OPENFILTERED); + break; + case SCTP_COOKIE_ECHO_SCAN: + (*target)->ports.setDefaultPortState(IPPROTO_SCTP, PORT_OPENFILTERED); + break; + case PING_SCAN: + case PING_SCAN_ARP: + break; + default: + fatal("Unexpected scan type found in %s()", __func__); + } + } +} + /* Order of initializations in this function CAN BE IMPORTANT, so be careful mucking with it. */ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, stype scantp) { @@ -1567,6 +1580,8 @@ void UltraScanInfo::Init(vector &Targets, struct scan_lists *pts, styp break; } + set_default_port_state(Targets, scantype); + init_perf_values(&perf); /* Keep a completed host around for a standard TCP MSL (2 min) */ @@ -2598,7 +2613,6 @@ static bool ultrascan_port_pspec_update(UltraScanInfo *USI, u16 portno = 0; u8 proto = 0; int oldstate = PORT_TESTING; - Port *currentp; /* Whether no response means a port is open */ bool noresp_open_scan = USI->noresp_open_scan; @@ -2616,13 +2630,11 @@ static bool ultrascan_port_pspec_update(UltraScanInfo *USI, portno = pspec->pd.sctp.dport; } else assert(0); - /* First figure out the current state */ - currentp = hss->target->ports.getPortEntry(portno, proto); - if (!currentp) { + oldstate = hss->target->ports.getPortState(portno, proto); + if (oldstate == -1) { oldstate = PORT_TESTING; hss->ports_finished++; } - else oldstate = currentp->state; /* printf("TCP port %hu has changed from state %s to %s!\n", portno, statenum2str(oldstate), statenum2str(newstate)); */ switch(oldstate) { @@ -2631,25 +2643,25 @@ static bool ultrascan_port_pspec_update(UltraScanInfo *USI, in a SYN scan, but not neccessarily for UDP scan */ case PORT_TESTING: /* Brand new port -- add it to the list */ - hss->target->ports.addPort(portno, proto, newstate); + hss->target->ports.setPortState(portno, proto, newstate); break; case PORT_OPEN: if (newstate != PORT_OPEN) { if (noresp_open_scan) { - hss->target->ports.addPort(portno, proto, newstate); + hss->target->ports.setPortState(portno, proto, newstate); } /* Otherwise The old open takes precendence */ } break; case PORT_CLOSED: if (newstate != PORT_CLOSED) { if (!noresp_open_scan && newstate != PORT_FILTERED) - hss->target->ports.addPort(portno, proto, newstate); + hss->target->ports.setPortState(portno, proto, newstate); } break; case PORT_FILTERED: if (newstate != PORT_FILTERED) { if (!noresp_open_scan || newstate != PORT_OPEN) - hss->target->ports.addPort(portno, proto, newstate); + hss->target->ports.setPortState(portno, proto, newstate); } break; case PORT_UNFILTERED: @@ -2658,11 +2670,11 @@ static bool ultrascan_port_pspec_update(UltraScanInfo *USI, case. I'll change it if the new state is open or closed, though I don't expect that to ever happen */ if (newstate == PORT_OPEN || newstate == PORT_CLOSED) - hss->target->ports.addPort(portno, proto, newstate); + hss->target->ports.setPortState(portno, proto, newstate); break; case PORT_OPENFILTERED: if (newstate != PORT_OPENFILTERED) { - hss->target->ports.addPort(portno, proto, newstate); + hss->target->ports.setPortState(portno, proto, newstate); } break; default: @@ -2700,18 +2712,16 @@ void HostScanStats::boostScanDelay() { sdn.goodRespSinceDelayChanged = 0; } -/* Dismiss all probe attempts on bench -- the ports are marked - 'filtered' or whatever is appropriate for having no response */ +/* Dismiss all probe attempts on bench -- hosts are marked down and ports will + be set to whatever the default port state is for the scan. */ void HostScanStats::dismissBench() { - int newstate; - if (probe_bench.empty()) return; - newstate = scantype_no_response_means(USI->scantype); while(!probe_bench.empty()) { if (USI->ping_scan) - ultrascan_host_pspec_update(USI, this, &probe_bench.back(), newstate); - else - ultrascan_port_pspec_update(USI, this, &probe_bench.back(), newstate); + ultrascan_host_pspec_update(USI, this, &probe_bench.back(), HOST_DOWN); + /* Nothing to do if !USI->ping_scan. ultrascan_port_pspec_update would + allocate a Port object but we rely on the default port state to save + memory. */ probe_bench.pop_back(); } bench_tryno = 0; @@ -2719,7 +2729,6 @@ void HostScanStats::dismissBench() { /* Move all members of bench to retry_stack for probe retransmission */ void HostScanStats::retransmitBench() { - int newstate; if (probe_bench.empty()) return; /* Move all contents of probe_bench to the end of retry_stack, updating retry_stack_tries accordingly */ @@ -2728,7 +2737,6 @@ void HostScanStats::retransmitBench() { bench_tryno); assert(retry_stack.size() == retry_stack_tries.size()); probe_bench.erase(probe_bench.begin(), probe_bench.end()); - newstate = scantype_no_response_means(USI->scantype); bench_tryno = 0; } @@ -4929,7 +4937,6 @@ static void processData(UltraScanInfo *USI) { list::iterator probeI, nextProbeI; HostScanStats *host = NULL; UltraProbe *probe = NULL; - int newstate; unsigned int maxtries = 0; bool scanmaybedone = true; /* The whole scan is not yet done */ int expire_us = 0; @@ -4995,19 +5002,20 @@ static void processData(UltraScanInfo *USI) { if (!probe->isPing() && probe->timedout && !probe->retransmitted) { if (!tryno_mayincrease && probe->tryno >= maxtries) { - newstate = scantype_no_response_means(USI->scantype); - if (USI->ping_scan) - ultrascan_host_probe_update(USI, host, probeI, newstate, NULL); - else - ultrascan_port_probe_update(USI, host, probeI, newstate, NULL); - if (host->target->reason.reason_id == ER_UNKNOWN) - host->target->reason.reason_id = ER_NORESPONSE; if (tryno_capped && !host->retry_capped_warned) { log_write(LOG_PLAIN, "Warning: %s giving up on port because" " retransmission cap hit (%d).\n", host->target->targetipstr(), probe->tryno); host->retry_capped_warned = true; } + if (USI->ping_scan) { + ultrascan_host_probe_update(USI, host, probeI, HOST_DOWN, NULL); + } else { + /* No ultrascan_port_probe_update because that allocates a Port + object; the default port state as set by setDefaultPortState + handles these no-response ports. */ + host->destroyOutstandingProbe(probeI); + } continue; } else if (probe->tryno >= maxtries && TIMEVAL_SUBTRACT(USI->now, probe->sent) > expire_us) { @@ -5258,8 +5266,8 @@ void bounce_scan(Target *target, u16 *portarray, int numports, perror("recv problem from FTP bounce server"); } else if (res == 0) { if (timedout) - target->ports.addPort(portarray[i], IPPROTO_TCP, PORT_FILTERED); - else target->ports.addPort(portarray[i], IPPROTO_TCP, PORT_CLOSED); + target->ports.setPortState(portarray[i], IPPROTO_TCP, PORT_FILTERED); + else target->ports.setPortState(portarray[i], IPPROTO_TCP, PORT_CLOSED); } else { recvbuf[res] = '\0'; if (o.debugging) log_write(LOG_STDOUT, "result of LIST: %s", recvbuf); @@ -5270,7 +5278,7 @@ void bounce_scan(Target *target, u16 *portarray, int numports, res = recvtime(sd, recvbuf, 2048,10, NULL); } if (recvbuf[0] == '1' || recvbuf[0] == '2') { - target->ports.addPort(portarray[i], IPPROTO_TCP, PORT_OPEN); + target->ports.setPortState(portarray[i], IPPROTO_TCP, PORT_OPEN); if (recvbuf[0] == '1') { res = recvtime(sd, recvbuf, 2048,5, NULL); if (res < 0) @@ -5281,7 +5289,7 @@ void bounce_scan(Target *target, u16 *portarray, int numports, if (o.debugging) log_write(LOG_STDOUT, "nxt line: %s", recvbuf); if (recvbuf[0] == '4' && recvbuf[1] == '2' && recvbuf[2] == '6') { - target->ports.removePort(portarray[i], IPPROTO_TCP); + target->ports.forgetPort(portarray[i], IPPROTO_TCP); if (o.debugging || o.verbose) log_write(LOG_STDOUT, "Changed my mind about port %i\n", portarray[i]); } @@ -5290,7 +5298,7 @@ void bounce_scan(Target *target, u16 *portarray, int numports, } } else { /* This means the port is closed ... */ - target->ports.addPort(portarray[i], IPPROTO_TCP, PORT_CLOSED); + target->ports.setPortState(portarray[i], IPPROTO_TCP, PORT_CLOSED); } } } @@ -5362,6 +5370,7 @@ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype) { unsigned long j; struct serviceDeductions sd; bool doingOpenFiltered = false; + Port port; ScanProgressMeter *SPM = NULL; @@ -5434,11 +5443,11 @@ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype) { while(1) { if (doingOpenFiltered) { rsi.rpc_current_port = target->ports.nextPort(rsi.rpc_current_port, - TCPANDUDPANDSCTP, + &port, TCPANDUDPANDSCTP, PORT_OPENFILTERED); } else { rsi.rpc_current_port = target->ports.nextPort(rsi.rpc_current_port, - TCPANDUDPANDSCTP, + &port, TCPANDUDPANDSCTP, PORT_OPEN); if (!rsi.rpc_current_port && !o.servicescan) { doingOpenFiltered = true; @@ -5452,7 +5461,7 @@ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype) { break; // We do all open ports if no service scan if (!rsi.rpc_current_port) break; // done! - rsi.rpc_current_port->getServiceDeductions(&sd); + target->ports.getServiceDeductions(rsi.rpc_current_port->portno, rsi.rpc_current_port->proto, &sd); if (sd.name && sd.service_tunnel == SERVICE_TUNNEL_NONE && strcmp(sd.name, "rpcbind") == 0) break; // Good - an RPC port for us to scan. @@ -5594,7 +5603,8 @@ void pos_scan(Target *target, u16 *portarray, int numports, stype scantype) { /* Now we figure out the results of the port we just RPC scanned */ - rsi.rpc_current_port->setRPCProbeResults(rsi.rpc_status, rsi.rpc_program, + target->ports.setRPCProbeResults(rsi.rpc_current_port->portno, rsi.rpc_current_port->proto, + rsi.rpc_status, rsi.rpc_program, rsi.rpc_lowver, rsi.rpc_highver); /* Time to put our RPC program scan list back together for the diff --git a/service_scan.cc b/service_scan.cc index 2c4e00312..877048162 100644 --- a/service_scan.cc +++ b/service_scan.cc @@ -149,7 +149,6 @@ public: // Note that the next 2 members are for convenience and are not destroyed w/the ServiceNFO Target *target; // the port belongs to this target host - Port *port; // The Port that this service represents (this copy is taken from inside Target) // if a match is found, it is placed here. Otherwise NULL const char *probe_matched; // If a match is found, any product/version/info/hostname/ostype/devicetype @@ -1339,7 +1338,6 @@ ServiceNFO::ServiceNFO(AllProbes *newAP) { AP = newAP; currentresp = NULL; currentresplen = 0; - port = NULL; product_matched[0] = version_matched[0] = extrainfo_matched[0] = '\0'; hostname_matched[0] = ostype_matched[0] = devicetype_matched[0] = '\0'; tunnel = SERVICE_TUNNEL_NONE; @@ -1633,6 +1631,7 @@ ServiceGroup::ServiceGroup(vector &Targets, AllProbes *AP) { unsigned int targetno; ServiceNFO *svc; Port *nxtport; + Port port; int desired_par; struct timeval now; num_hosts_timedout = 0; @@ -1644,12 +1643,11 @@ ServiceGroup::ServiceGroup(vector &Targets, AllProbes *AP) { num_hosts_timedout++; continue; } - while((nxtport = Targets[targetno]->ports.nextPort(nxtport, TCPANDUDPANDSCTP, PORT_OPEN))) { + while((nxtport = Targets[targetno]->ports.nextPort(nxtport, &port, TCPANDUDPANDSCTP, PORT_OPEN))) { svc = new ServiceNFO(AP); svc->target = Targets[targetno]; svc->portno = nxtport->portno; svc->proto = nxtport->proto; - svc->port = nxtport; services_remaining.push_back(svc); } } @@ -1662,12 +1660,11 @@ ServiceGroup::ServiceGroup(vector &Targets, AllProbes *AP) { if (Targets[targetno]->timedOut(&now)) { continue; } - while((nxtport = Targets[targetno]->ports.nextPort(nxtport, TCPANDUDPANDSCTP, PORT_OPENFILTERED))) { + while((nxtport = Targets[targetno]->ports.nextPort(nxtport, &port, TCPANDUDPANDSCTP, PORT_OPENFILTERED))) { svc = new ServiceNFO(AP); svc->target = Targets[targetno]; svc->portno = nxtport->portno; svc->proto = nxtport->proto; - svc->port = nxtport; services_remaining.push_back(svc); } } @@ -1703,8 +1700,8 @@ static void adjustPortStateIfNeccessary(ServiceNFO *svc) { char host[128]; - if (svc->port->state == PORT_OPENFILTERED) { - svc->target->ports.addPort(svc->portno, svc->proto, PORT_OPEN); + if (svc->target->ports.getPortState(svc->portno, svc->proto) == PORT_OPENFILTERED) { + svc->target->ports.setPortState(svc->portno, svc->proto, PORT_OPEN); if (svc->proto == IPPROTO_TCP) svc->target->ports.setStateReason(svc->portno, svc->proto, ER_TCPRESPONSE, 0, 0); if (svc->proto == IPPROTO_UDP) @@ -2318,7 +2315,8 @@ list::iterator svc; for(svc = SG->services_finished.begin(); svc != SG->services_finished.end(); svc++) { if ((*svc)->probe_state != PROBESTATE_FINISHED_NOMATCH) { - (*svc)->port->setServiceProbeResults((*svc)->probe_state, + (*svc)->target->ports.setServiceProbeResults((*svc)->portno, (*svc)->proto, + (*svc)->probe_state, (*svc)->probe_matched, (*svc)->tunnel, *(*svc)->product_matched? (*svc)->product_matched : NULL, @@ -2329,7 +2327,8 @@ list::iterator svc; *(*svc)->devicetype_matched? (*svc)->devicetype_matched : NULL, shouldWePrintFingerprint(*svc) ? (*svc)->getServiceFingerprint(NULL) : NULL); } else { - (*svc)->port->setServiceProbeResults((*svc)->probe_state, NULL, + (*svc)->target->ports.setServiceProbeResults((*svc)->portno, (*svc)->proto, + (*svc)->probe_state, NULL, (*svc)->tunnel, NULL, NULL, NULL, NULL, NULL, NULL, (*svc)->getServiceFingerprint(NULL)); } @@ -2372,7 +2371,8 @@ static void remove_excluded_ports(AllProbes *AP, ServiceGroup *SG) { if (o.debugging) log_write(LOG_PLAIN, "EXCLUDING %d/%s\n", svc->portno, IPPROTO2STR(svc->proto)); - svc->port->setServiceProbeResults(PROBESTATE_EXCLUDED, NULL, + svc->target->ports.setServiceProbeResults(svc->portno, svc->proto, + PROBESTATE_EXCLUDED, NULL, SERVICE_TUNNEL_NONE, "Excluded from version scan", NULL, NULL, NULL, NULL, NULL, NULL);