diff --git a/services.cc b/services.cc index 06f6c2253..a1a945167 100644 --- a/services.cc +++ b/services.cc @@ -99,17 +99,49 @@ /* $Id$ */ +#include +#include + #include "services.h" #include "NmapOps.h" #include "charpool.h" #include "nmap_error.h" #include "utils.h" +/* This structure is the key for looking up services in the + port/proto -> service map. */ +struct port_spec { + int portno; /* Network byte order */ + std::string proto; + + /* Sort in the usual nmap-services order. */ + bool operator<(const port_spec& other) const { + if (this->portno < other.portno) + return true; + else if (this->portno > other.portno) + return false; + else + return this->proto < other.proto; + } +}; + +/* This is a servent augmented by a frequency ratio. */ +struct service_node : public servent { +public: + double ratio; +}; + +/* Compare the ratios of two service nodes for top-ports purposes. Larger ratios + come before smaller. */ +bool service_node_ratio_compare(const service_node& a, const service_node& b) { + return a.ratio > b.ratio; +} + extern NmapOps o; static int numtcpports = 0; static int numudpports = 0; -static struct service_list *service_table[SERVICE_TABLE_SIZE]; -static struct service_list *sorted_services = NULL; +static std::map service_table; +static std::list services_by_ratio; static int services_initialized = 0; static int ratio_format = 0; // 0 = /etc/services no-ratio format. 1 = new nmap format @@ -123,7 +155,6 @@ static int nmap_services_init() { char *p; char line[1024]; int lineno = 0; - struct service_list *current, *previous, *sp; int res; double ratio; int ratio_n, ratio_d; @@ -159,8 +190,6 @@ static int nmap_services_init() { /* Record where this data file was found. */ o.loaded_data_files["nmap-services"] = filename; - memset(service_table, 0, sizeof(service_table)); - while(fgets(line, sizeof(line), fp)) { lineno++; p = line; @@ -191,21 +220,18 @@ static int nmap_services_init() { portno = htons(portno); + port_spec ps; + ps.portno = portno; + ps.proto = proto; + /* Now we make sure our service table doesn't have duplicates */ - for(current = service_table[portno % SERVICE_TABLE_SIZE], previous = NULL; - current; current = current->next) { - if (portno == (u16) current->servent->s_port && - strcasecmp(proto, current->servent->s_proto) == 0) { - if (o.debugging) { - error("Port %d proto %s is duplicated in services file %s", ntohs(portno), proto, filename); - } - break; - } - previous = current; - } - /* Current service in the file was a duplicate, get another one */ - if (current) + std::map::iterator i; + i = service_table.find(ps); + if (i != service_table.end()) { + if (o.debugging) + error("Port %d proto %s is duplicated in services file %s", ntohs(portno), proto, filename); continue; + } if (strncasecmp(proto, "tcp", 3) == 0) { numtcpports++; @@ -223,42 +249,22 @@ static int nmap_services_init() { continue; } - current = (struct service_list *) cp_alloc(sizeof(struct service_list)); - current->servent = (struct servent *) cp_alloc(sizeof(struct servent)); - current->ratio = ratio; - current->next = NULL; - if (previous == NULL) { - service_table[portno % SERVICE_TABLE_SIZE] = current; - } else { - previous->next = current; - } - current->servent->s_name = cp_strdup(servicename); - current->servent->s_port = portno; - current->servent->s_proto = cp_strdup(proto); - current->servent->s_aliases = NULL; + struct service_node sn; - sp = (struct service_list *) cp_alloc(sizeof(struct service_list)); - sp->servent = current->servent; - sp->ratio = current->ratio; - sp->next = NULL; + sn.s_name = cp_strdup(servicename); + sn.s_port = portno; + sn.s_proto = cp_strdup(proto); + sn.s_aliases = NULL; + sn.ratio = ratio; - if (sorted_services == NULL || sorted_services->ratio < sp->ratio) { - sp->next = sorted_services; - sorted_services = sp; - } else - for (current=sorted_services;;current=current->next) { - if (current->next == NULL) { - current->next = sp; - break; - } else if (current->next->ratio < sp->ratio) { - sp->next = current->next; - current->next = sp; - break; - } - } + service_table[ps] = sn; + services_by_ratio.push_back(sn); } + /* Sort the list of ports sorted by frequency for top-ports purposes. */ + services_by_ratio.sort(service_node_ratio_compare); + fclose(fp); services_initialized = 1; return 0; @@ -273,52 +279,47 @@ static int nmap_services_init() { */ int addportsfromservmask(char *mask, u8 *porttbl, int range_type) { - struct service_list *current; - int bucket,t=0; + std::map::iterator i; + int t = 0; if (!services_initialized && nmap_services_init() == -1) fatal("%s: Couldn't get port numbers", __func__); - for(bucket = 0; bucket < SERVICE_TABLE_SIZE; bucket++) { - for(current = service_table[bucket % SERVICE_TABLE_SIZE]; current; current = current->next) { - if (wildtest(mask, current->servent->s_name)) { - - if ((range_type & SCAN_TCP_PORT) && strcmp(current->servent->s_proto, "tcp") == 0) { - porttbl[ntohs(current->servent->s_port)] |= SCAN_TCP_PORT; - t++; - } - - if ((range_type & SCAN_UDP_PORT) && strcmp(current->servent->s_proto, "udp") == 0) { - porttbl[ntohs(current->servent->s_port)] |= SCAN_UDP_PORT; - t++; - } - + for (i = service_table.begin(); i != service_table.end(); i++) { + service_node& current = i->second; + if (wildtest(mask, current.s_name)) { + if ((range_type & SCAN_TCP_PORT) && strcmp(current.s_proto, "tcp") == 0) { + porttbl[ntohs(current.s_port)] |= SCAN_TCP_PORT; + t++; + } + if ((range_type & SCAN_UDP_PORT) && strcmp(current.s_proto, "udp") == 0) { + porttbl[ntohs(current.s_port)] |= SCAN_UDP_PORT; + t++; } } } return t; - } +/* Port must be in network byte order. */ struct servent *nmap_getservbyport(int port, const char *proto) { - struct service_list *current; + std::map::iterator i; + port_spec ps; if (nmap_services_init() == -1) return NULL; - for(current = service_table[port % SERVICE_TABLE_SIZE]; - current; current = current->next) { - if (((u16) port == (u16) current->servent->s_port) && - strcmp(proto, current->servent->s_proto) == 0) - return current->servent; - } + ps.portno = port; + ps.proto = proto; + i = service_table.find(ps); + if (i != service_table.end()) + return &i->second; /* Couldn't find it ... oh well. */ return NULL; - } @@ -336,18 +337,20 @@ static int port_compare(const void *a, const void *b) { // called when you use a non-default top-ports or port-ratio value TOGETHER WITH // a -p portlist. -static int is_port_member(struct scan_lists *ptsdata, struct service_list *serv) { +static bool is_port_member(const struct scan_lists *ptsdata, const struct service_node *serv) { int i; - if (serv->servent->s_proto[0] == 't') { + if (strcmp(serv->s_proto, "tcp") == 0) { for (i=0; itcp_count; i++) - if (ntohs(serv->servent->s_port) == ptsdata->tcp_ports[i]) return 1; - } else { + if (ntohs(serv->s_port) == ptsdata->tcp_ports[i]) + return true; + } else if (strcmp(serv->s_proto, "udp") == 0) { for (i=0; iudp_count; i++) - if (ntohs(serv->servent->s_port) == ptsdata->udp_ports[i]) return 1; + if (ntohs(serv->s_port) == ptsdata->udp_ports[i]) + return true; } - return 0; + return false; } // gettoppts() sets its third parameter, a scan_list, with the most @@ -367,7 +370,8 @@ void gettoppts(double level, char *portlist, struct scan_lists * ports) { int ti=0, ui=0; struct scan_lists ptsdata = { 0 }; bool ptsdata_initialized = false; - struct service_list *current; + const struct service_node *current; + std::list::iterator i; if (!services_initialized && nmap_services_init() == -1) fatal("%s: Couldn't get port numbers", __func__); @@ -399,60 +403,68 @@ void gettoppts(double level, char *portlist, struct scan_lists * ports) { } if (portlist){ - getpts(portlist, &ptsdata); - ptsdata_initialized = true; + getpts(portlist, &ptsdata); + ptsdata_initialized = true; } if (level < 1) { - for (current=sorted_services; current; current=current->next) { - if (ptsdata_initialized && !is_port_member(&ptsdata, current)) continue; - + for (i = services_by_ratio.begin(); i != services_by_ratio.end(); i++) { + current = &(*i); + if (ptsdata_initialized && !is_port_member(&ptsdata, current)) + continue; if (current->ratio >= level) { - if (o.TCPScan() && current->servent->s_proto[0] == 't') ports->tcp_count++; - else if (o.UDPScan() && current->servent->s_proto[0] == 'u') ports->udp_count++; - } else break; + if (o.TCPScan() && strcmp(current->s_proto, "tcp") == 0) + ports->tcp_count++; + else if (o.UDPScan() && strcmp(current->s_proto, "udp") == 0) + ports->udp_count++; + } else { + break; + } } if (ports->tcp_count) - ports->tcp_ports = (unsigned short *)safe_zalloc(ports->tcp_count * sizeof(unsigned short)); + ports->tcp_ports = (unsigned short *)safe_zalloc(ports->tcp_count * sizeof(unsigned short)); if (ports->udp_count) - ports->udp_ports = (unsigned short *)safe_zalloc(ports->udp_count * sizeof(unsigned short)); + ports->udp_ports = (unsigned short *)safe_zalloc(ports->udp_count * sizeof(unsigned short)); ports->prots = NULL; - for (current=sorted_services;current;current=current->next) { - if (ptsdata_initialized && !is_port_member(&ptsdata, current)) continue; - + for (i = services_by_ratio.begin(); i != services_by_ratio.end(); i++) { + current = &(*i); + if (ptsdata_initialized && !is_port_member(&ptsdata, current)) + continue; if (current->ratio >= level) { - if (o.TCPScan() && current->servent->s_proto[0] == 't') - ports->tcp_ports[ti++] = ntohs(current->servent->s_port); - else if (o.UDPScan() && current->servent->s_proto[0] == 'u') - ports->udp_ports[ui++] = ntohs(current->servent->s_port); - } else break; + if (o.TCPScan() && strcmp(current->s_proto, "tcp") == 0) + ports->tcp_ports[ti++] = ntohs(current->s_port); + else if (o.UDPScan() && strcmp(current->s_proto, "udp") == 0) + ports->udp_ports[ui++] = ntohs(current->s_port); + } else { + break; + } } } else if (level >= 1) { if (level > 65536) fatal("Level argument to gettoppts (%g) is too large", level); if (o.TCPScan()) { - ports->tcp_count = MIN((int) level, numtcpports); - ports->tcp_ports = (unsigned short *)safe_zalloc(ports->tcp_count * sizeof(unsigned short)); + ports->tcp_count = MIN((int) level, numtcpports); + ports->tcp_ports = (unsigned short *)safe_zalloc(ports->tcp_count * sizeof(unsigned short)); } - if (o.UDPScan()) { - ports->udp_count = MIN((int) level, numudpports); - ports->udp_ports = (unsigned short *)safe_zalloc(ports->udp_count * sizeof(unsigned short)); + ports->udp_count = MIN((int) level, numudpports); + ports->udp_ports = (unsigned short *)safe_zalloc(ports->udp_count * sizeof(unsigned short)); } ports->prots = NULL; - for (current=sorted_services;current && (ti < ports->tcp_count || ui < ports->udp_count);current=current->next) { - if (ptsdata_initialized && !is_port_member(&ptsdata, current)) continue; - - if (o.TCPScan() && current->servent->s_proto[0] == 't' && ti < ports->tcp_count) - ports->tcp_ports[ti++] = ntohs(current->servent->s_port); - else if (o.UDPScan() && current->servent->s_proto[0] == 'u' && ui < ports->udp_count) - ports->udp_ports[ui++] = ntohs(current->servent->s_port); + for (i = services_by_ratio.begin(); i != services_by_ratio.end(); i++) { + current = &(*i); + if (ptsdata_initialized && !is_port_member(&ptsdata, current)) + continue; + if (o.TCPScan() && strcmp(current->s_proto, "tcp") == 0 && ti < ports->tcp_count) + ports->tcp_ports[ti++] = ntohs(current->s_port); + else if (o.UDPScan() && strcmp(current->s_proto, "udp") == 0 && ui < ports->udp_count) + ports->udp_ports[ui++] = ntohs(current->s_port); } if (ti < ports->tcp_count) ports->tcp_count = ti; @@ -466,14 +478,14 @@ void gettoppts(double level, char *portlist, struct scan_lists * ports) { } if (ports->tcp_count > 1) - qsort(ports->tcp_ports, ports->tcp_count, sizeof(unsigned short), &port_compare); + qsort(ports->tcp_ports, ports->tcp_count, sizeof(unsigned short), &port_compare); if (ports->udp_count > 1) - qsort(ports->udp_ports, ports->udp_count, sizeof(unsigned short), &port_compare); + qsort(ports->udp_ports, ports->udp_count, sizeof(unsigned short), &port_compare); if (o.debugging && level < 1) - log_write(LOG_STDOUT, "PORTS: Using ports open on %g%% or more average hosts (TCP:%d, UDP:%d)\n", level*100, ports->tcp_count, ports->udp_count); + log_write(LOG_STDOUT, "PORTS: Using ports open on %g%% or more average hosts (TCP:%d, UDP:%d)\n", level*100, ports->tcp_count, ports->udp_count); else if (o.debugging && level >= 1) - log_write(LOG_STDOUT, "PORTS: Using top %d ports found open (TCP:%d, UDP:%d)\n", (int) level, ports->tcp_count, ports->udp_count); + log_write(LOG_STDOUT, "PORTS: Using top %d ports found open (TCP:%d, UDP:%d)\n", (int) level, ports->tcp_count, ports->udp_count); } diff --git a/services.h b/services.h index 4283673b1..a9edeb296 100644 --- a/services.h +++ b/services.h @@ -122,12 +122,6 @@ #define SCAN_UDP_PORT (1 << 1) #define SCAN_PROTOCOLS (1 << 2) -struct service_list { - struct servent *servent; - double ratio; - struct service_list *next; -}; - int addportsfromservmask(char *mask, u8 *porttbl, int range_type); struct servent *nmap_getservbyport(int port, const char *proto); void gettoppts(double level, char *portlist, struct scan_lists * ports);