From e51abafe02017870ca59a1549f3d64cb06f27ca9 Mon Sep 17 00:00:00 2001 From: fyodor Date: Sat, 11 Aug 2007 02:58:03 +0000 Subject: [PATCH] merge soc07 r4727 - Added --top-ports/--port-ratio and wildcard/[] -p switch extensions. --- NmapOps.cc | 2 + NmapOps.h | 2 + nmap.cc | 299 +++++++++++++++++++++++++++++++++------------ protocols.cc | 90 +++++--------- protocols.h | 4 +- services.cc | 339 ++++++++++++++++++++++++++++++++++----------------- services.h | 5 +- utils.cc | 43 +++++++ utils.h | 2 + 9 files changed, 528 insertions(+), 258 deletions(-) diff --git a/NmapOps.cc b/NmapOps.cc index 0f3ef8a02..cfa0f6de1 100644 --- a/NmapOps.cc +++ b/NmapOps.cc @@ -192,6 +192,7 @@ void NmapOps::Initialize() { randomize_hosts = 0; sendpref = PACKET_SEND_NOPREF; spoofsource = 0; + fastscan = 0; device[0] = '\0'; interactivemode = 0; ping_group_sz = PING_GROUP_SZ; @@ -261,6 +262,7 @@ void NmapOps::Initialize() { ipopt_firsthop = 0; ipopt_lasthop = 0; release_memory = false; + topportlevel = -1; #ifndef NOLUA script = 0; scriptversion = 0; diff --git a/NmapOps.h b/NmapOps.h index 005cb332d..9a016f836 100644 --- a/NmapOps.h +++ b/NmapOps.h @@ -172,6 +172,7 @@ class NmapOps { int verbose; int randomize_hosts; int spoofsource; /* -S used */ + int fastscan; char device[64]; int interactivemode; int ping_group_sz; @@ -191,6 +192,7 @@ class NmapOps { int timing_level; // 0-5, corresponding to Paranoid, Sneaky, Polite, Normal, Aggressive, Insane int max_parallelism; // 0 means it has not been set int min_parallelism; // 0 means it has not been set + double topportlevel; // -1 means it has not been set /* The maximum number of OS detection (gen2) tries we will make without any matches before giving up on a host. We may well give diff --git a/nmap.cc b/nmap.cc index dd1f6ed30..59d3ae842 100644 --- a/nmap.cc +++ b/nmap.cc @@ -230,8 +230,10 @@ printf("%s %s ( %s )\n" "PORT SPECIFICATION AND SCAN ORDER:\n" " -p : Only scan specified ports\n" " Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080\n" - " -F: Fast - Scan only the ports listed in the nmap-services file)\n" + " -F: Fast mode - Scan fewer ports than the default scan\n" " -r: Scan ports consecutively - don't randomize\n" + " --top-ports : Scan most common ports\n" + " --port-ratio : Scan ports more common than \n" "SERVICE/VERSION DETECTION:\n" " -sV: Probe open ports to determine service/version info\n" " --version-intensity : Set from 0 (light) to 9 (try all probes)\n" @@ -451,7 +453,7 @@ int nmap_main(int argc, char *argv[]) { unsigned int targetno; FILE *inputfd = NULL, *excludefd = NULL; char *host_spec = NULL, *exclude_spec = NULL; - short fastscan=0, randomize=1; + short randomize=1; short quashargv = 0; char **host_exp_group; char *idleProxy = NULL; /* The idle host used to "Proxy" an Idlescan */ @@ -600,6 +602,8 @@ int nmap_main(int argc, char *argv[]) { {"log-errors", no_argument, 0, 0}, {"dns_servers", required_argument, 0, 0}, {"dns-servers", required_argument, 0, 0}, + {"port-ratio", required_argument, 0, 0}, + {"top-ports", required_argument, 0, 0}, #ifndef NOLUA {"script", required_argument, 0, 0}, {"script-trace", no_argument, 0, 0}, @@ -829,6 +833,16 @@ int nmap_main(int argc, char *argv[]) { o.fragscan = atoi(optarg); if (o.fragscan <= 0 || o.fragscan % 8 != 0) fatal("Data payload MTU must be >0 and multiple of 8"); + } else if (strcmp(long_options[option_index].name, "port-ratio") == 0) { + char *ptr; + o.topportlevel = strtod(optarg, &ptr); + if (!ptr || o.topportlevel < 0 || o.topportlevel >= 1) + fatal("--port-ratio should be between [0 and 1)"); + } else if (strcmp(long_options[option_index].name, "top-ports") == 0) { + char *ptr; + o.topportlevel = strtod(optarg, &ptr); + if (!ptr || o.topportlevel < 1 || ((double)((int)o.topportlevel)) != o.topportlevel) + fatal("--top-ports should be an integer 1 or greater"); } else if (optcmp(long_options[option_index].name, "ip-options") == 0){ o.ipoptions = (u8*) safe_malloc(4*10+1); o.ipoptionslen = parse_ip_options(optarg, o.ipoptions, 4*10+1, &o.ipopt_firsthop, &o.ipopt_lasthop); @@ -896,7 +910,7 @@ int nmap_main(int argc, char *argv[]) { break; case 'e': Strncpy(o.device, optarg, sizeof(o.device)); break; - case 'F': fastscan++; break; + case 'F': o.fastscan++; break; case 'f': o.fragscan += 8; break; case 'g': o.magic_port = atoi(optarg); @@ -1202,25 +1216,29 @@ int nmap_main(int argc, char *argv[]) { } - if ((o.pingscan || o.listscan) && (portlist || fastscan)) { + if ((o.pingscan || o.listscan) && (portlist || o.fastscan)) fatal("You cannot use -F (fast scan) or -p (explicit port selection) with PING scan or LIST scan"); + + if (portlist && o.fastscan) + fatal("You cannot use -F (fast scan) with -p (explicit port selection) but see --top-ports and --port-ratio to fast scan a range of ports"); + + if (o.ipprotscan) { + if (portlist) ports = getpts(portlist); + else ports = getpts((char *) (o.fastscan ? "[P:0-]" : "0-")); // Default protocols to scan + } else { + ports = gettoppts(o.topportlevel, portlist); } + if (portlist && !ports) + fatal("Your port specification string is not parseable"); + if (portlist) { - ports = getpts(portlist); - if (!ports) - fatal("Your port specification string is not parseable"); free(portlist); portlist = NULL; } - if (fastscan && ports) { - fatal("You can specify fast scan (-F) or explicitly select individual ports (-p), but not both"); - } else if (fastscan && o.ipprotscan) { - ports = getfastprots(); - } else if (fastscan) { - ports = getfastports(o.TCPScan(), o.UDPScan()); - } + // Uncomment the following line to use the common lisp port spec test suite + //printf("port spec: (%d %d %d)\n", ports->tcp_count, ports->udp_count, ports->prot_count); exit(0); #ifdef WIN32 if (o.sendpref & PACKET_SEND_IP) { @@ -1274,14 +1292,6 @@ int nmap_main(int argc, char *argv[]) { o.sendpref = PACKET_SEND_ETH_STRONG; } - if (!ports) { - if (o.ipprotscan) { - ports = getdefaultprots(); - } else { - ports = getdefaultports(o.TCPScan(), o.UDPScan()); - } - } - /* By now, we've got our port lists. Give the user a warning if no * ports are specified for the type of scan being requested. Other things * (such as OS ident scan) might break cause no ports were specified, but @@ -1930,19 +1940,74 @@ void init_socket(int sd) { } } -/* Convert a string like "-100,200-1024,3000-4000,60000-" into an array - of port numbers. Note that one trailing comma is OK -- this is actually - useful for machine generated lists */ + + +/* Convert a string like "-100,n*tp,200-1024,3000-4000,[60000-]" into an array + * of port numbers. Note that one trailing comma is OK -- this is actually + * useful for machine generated lists + * + * Fyodor - Wrote original + * William McVey - Added T:, U:, P: directives + * Doug Hoyte - Added [], name lookups, and wildcard expansion + * + * getpts() handles [] + * Any port ranges included inside square brackets will have all + * their ports looked up in nmap-services or nmap-protocols + * and will only be included if they are found. + * Returns a scan_list* with all the ports that should be scanned. + * + * getpts() handles service/protocol name lookups and wildcard expansion. + * The service name can be specified instead of the port number. + * For example, "ssh" can be used instead of "22". You can use wildcards + * like "*" and "?". See the function wildtest() for the exact details. + * For example, + * + * nmap -p http* host + * + * Will scan http (80), http-mgmt (280), http-proxy (8080), https (443), etc. + * + * Matching is case INsensitive but the first character in a match MUST + * be lowercase so it doesn't conflict with the T:, U:, and P: directives. + * + * getpts() is unable to match service names that start with a digit + * like 3com-tsmux (106/udp). Use a pattern like "?com-*" instead. + * + * BE CAREFUL ABOUT SHELL EXPANSIONS!!! + * If you are trying to match the services nmsp (537/tcp) and nms (1429/tcp) + * and you execute the command + * + * ./nmap -p nm* host + * + * You will see + * + * Found no matches for the service mask 'nmap' and your specified protocols + * QUITTING! + * + * This is because nm* was expanded to the name of the binary file nmap in + * the current directory by your shell. When unsure, quote your port strings + * to be safe: + * + * ./nmap -p 'nm*' host + * + * getpts() is smart enough to keep the T: U: and P: directives nested + * and working in a logical manner. For instance, + * + * nmap -sTU -p [U:1025-],1-1024 host + * + * Will scan UDP ports 1025 and up that are found in the service file + * and all TCP/UDP ports below <= 1024. Notice that the U doesn't affect + * the outer part of the port expression. It's "closed". + */ + +static void getpts_aux(char *origexpr, int nested, u8 *porttbl, struct scan_lists *ports, int range_type, int +*portwarning); + struct scan_lists *getpts(char *origexpr) { u8 *porttbl; - int portwarning = 0; /* have we warned idiot about dup ports yet? */ - long rangestart = -2343242, rangeend = -9324423; - char *current_range; - char *endptr; - int i; - int tcpportcount = 0, udpportcount = 0, protcount = 0; struct scan_lists *ports; int range_type = 0; + int portwarning = 0; + int i, tcpi, udpi, proti; if (o.TCPScan()) range_type |= SCAN_TCP_PORT; @@ -1952,11 +2017,58 @@ struct scan_lists *getpts(char *origexpr) { range_type |= SCAN_PROTOCOLS; porttbl = (u8 *) safe_zalloc(65536); + ports = (struct scan_lists *) safe_zalloc(sizeof(struct scan_lists)); + + getpts_aux(origexpr, // Pass on the expression + 0, // Don't start off nested + porttbl, // Our allocated port table + ports, // The destination structure - passed so we can track the number of tcp/udp/prot ports + range_type, // Defaults to TCP/UDP/Protos + &portwarning); // No, we haven't warned them about dup ports yet + + if ( 0 == (ports->tcp_count + ports->udp_count + ports->prot_count)) + fatal("No ports specified -- If you really don't want to scan any ports use ping scan..."); + + if (ports->tcp_count) { + 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)); + } + if (ports->prot_count) { + ports->prots = (unsigned short *)safe_zalloc(ports->prot_count * sizeof(unsigned short)); + } + + for(i=tcpi=udpi=proti=0; i <= 65535; i++) { + if (porttbl[i] & SCAN_TCP_PORT) + ports->tcp_ports[tcpi++] = i; + if (porttbl[i] & SCAN_UDP_PORT) + ports->udp_ports[udpi++] = i; + if (porttbl[i] & SCAN_PROTOCOLS && i < 256) + ports->prots[proti++] = i; + } + + free(porttbl); + + return ports; + +} + + +/* getpts() (see above) is a wrapper for this function */ + +static void getpts_aux(char *origexpr, int nested, u8 *porttbl, struct scan_lists *ports, int range_type, int *portwarning) { + long rangestart = -2343242, rangeend = -9324423; + char *current_range; + char *endptr; + char servmask[128]; // A protocol name can be up to 127 chars + nul byte + int i; current_range = origexpr; do { while(isspace((int) *current_range)) current_range++; /* I don't know why I should allow spaces here, but I will */ + if (*current_range == 'T' && *++current_range == ':') { current_range++; range_type = SCAN_TCP_PORT; @@ -1972,7 +2084,26 @@ struct scan_lists *getpts(char *origexpr) { range_type = SCAN_PROTOCOLS; continue; } - if (*current_range == '-') { + if (*current_range == '[') { + if (nested) + fatal("Can't nest [] brackets in -p switch"); + + getpts_aux(++current_range, 1, porttbl, ports, range_type, portwarning); + + // Skip past the ']'. This is OK because we can't nest []s + while(*current_range != ']') current_range++; + current_range++; + + // Skip over a following ',' so we're ready to keep parsing + if (*current_range == ',') current_range++; + + continue; + } else if (*current_range == ']') { + if (!nested) + fatal("Unexpected ] character in -p switch"); + + return; + } else if (*current_range == '-') { rangestart = o.ipprotscan ? 0 : 1; } else if (isdigit((int) *current_range)) { @@ -1984,21 +2115,38 @@ struct scan_lists *getpts(char *origexpr) { if (rangestart < 0 || rangestart > 65535) fatal("Ports to be scanned must be between 0 and 65535 inclusive"); } -/* if (rangestart == 0) { - error("WARNING: Scanning \"port 0\" is supported, but unusual."); - } */ current_range = endptr; while(isspace((int) *current_range)) current_range++; + } else if (islower((int) *current_range) || *current_range == '*' || *current_range == '?') { + i = 0; + + while (*current_range && !isspace((int)*current_range) && *current_range != ',' && *current_range != ']') { + servmask[i++] = *(current_range++); + if (i >= ((int)sizeof(servmask)-1)) + fatal("A service mask in the -p switch is either malformed or too long"); + } + + if (*current_range && *current_range != ']') current_range++; // We want the '] character to be picked up on the next pass + servmask[i] = '\0'; // Finish the string + + i = addportsfromservmask(servmask, porttbl, ports, range_type); + if (range_type & SCAN_PROTOCOLS) i += addprotocolsfromservmask(servmask, porttbl, ports); + + if (i == 0) + fatal("Found no matches for the service mask '%s' and your specified protocols", servmask); + + continue; + } else { fatal("Error #485: Your port specifications are illegal. Example of proper form: \"-100,200-1024,T:3000-4000,U:60000-\""); } /* Now I have a rangestart, time to go after rangeend */ - if (!*current_range || *current_range == ',') { + if (!*current_range || *current_range == ',' || *current_range == ']') { /* Single port specification */ rangeend = rangestart; } else if (*current_range == '-') { current_range++; - if (!*current_range || *current_range == ',') { + if (!*current_range || *current_range == ',' || *current_range == ']') { /* Ended with a -, meaning up until the last possible port */ rangeend = o.ipprotscan ? 255 : 65535; } else if (isdigit((int) *current_range)) { @@ -2021,24 +2169,45 @@ struct scan_lists *getpts(char *origexpr) { /* Now I have a rangestart and a rangeend, so I can add these ports */ while(rangestart <= rangeend) { if (porttbl[rangestart] & range_type) { - if (!portwarning) { + if (!(*portwarning)) { error("WARNING: Duplicate port number(s) specified. Are you alert enough to be using Nmap? Have some coffee or Jolt(tm)."); - portwarning++; + (*portwarning)++; } } else { - if (range_type & SCAN_TCP_PORT) - tcpportcount++; - if (range_type & SCAN_UDP_PORT) - udpportcount++; - if (range_type & SCAN_PROTOCOLS) - protcount++; - porttbl[rangestart] |= range_type; + if (nested) { + if ((range_type & SCAN_TCP_PORT) && + nmap_getservbyport(htons(rangestart), "tcp")) { + ports->tcp_count++; + porttbl[rangestart] |= SCAN_TCP_PORT; + } + if ((range_type & SCAN_UDP_PORT) && + nmap_getservbyport(htons(rangestart), "udp")) { + ports->udp_count++; + porttbl[rangestart] |= SCAN_UDP_PORT; + } + if ((range_type & SCAN_PROTOCOLS) && + nmap_getprotbynum(htons(rangestart))) { + ports->prot_count++; + porttbl[rangestart] |= SCAN_PROTOCOLS; + } + } else { + if (range_type & SCAN_TCP_PORT) + ports->tcp_count++; + if (range_type & SCAN_UDP_PORT) + ports->udp_count++; + if (range_type & SCAN_PROTOCOLS && rangestart < 256) + ports->prot_count++; + porttbl[rangestart] |= range_type; + } } rangestart++; } /* Find the next range */ while(isspace((int) *current_range)) current_range++; + + if (*current_range == ']') return; + if (*current_range && *current_range != ',') { fatal("Error #488: Your port specifications are illegal. Example of proper form: \"-100,200-1024,3000-4000,60000-\""); } @@ -2046,45 +2215,13 @@ struct scan_lists *getpts(char *origexpr) { current_range++; } while(current_range && *current_range); - if ( 0 == (tcpportcount + udpportcount + protcount)) - fatal("No ports specified -- If you really don't want to scan any ports use ping scan..."); - - ports = (struct scan_lists *) safe_zalloc(sizeof(struct scan_lists)); - - if (tcpportcount) { - ports->tcp_ports = (unsigned short *)safe_zalloc(tcpportcount * sizeof(unsigned short)); - } - if (udpportcount) { - ports->udp_ports = (unsigned short *)safe_zalloc(udpportcount * sizeof(unsigned short)); - } - if (protcount) { - ports->prots = (unsigned short *)safe_zalloc(protcount * sizeof(unsigned short)); - } - ports->tcp_count = tcpportcount; - ports->udp_count = udpportcount; - ports->prot_count = protcount; - - tcpportcount=0; - udpportcount=0; - protcount=0; - for(i=0; i <= 65535; i++) { - if (porttbl[i] & SCAN_TCP_PORT) - ports->tcp_ports[tcpportcount++] = i; - if (porttbl[i] & SCAN_UDP_PORT) - ports->udp_ports[udpportcount++] = i; - if (porttbl[i] & SCAN_PROTOCOLS && i < 256) - ports->prots[protcount++] = i; - } - - free(porttbl); - return ports; } void free_scan_lists(struct scan_lists *ports) { if (ports) { - free(ports->tcp_ports); - free(ports->udp_ports); - free(ports->prots); + if (ports->tcp_ports) free(ports->tcp_ports); + if (ports->udp_ports) free(ports->udp_ports); + if (ports->prots) free(ports->prots); free(ports); } } diff --git a/protocols.cc b/protocols.cc index b9eaf38f0..aa0d6c234 100644 --- a/protocols.cc +++ b/protocols.cc @@ -178,9 +178,40 @@ static int nmap_protocols_init() { } +/* Adds protocols whose names match mask to porttbl. + * Increases the prot_count in ports by the number of protocols added. + * Returns the number of protocols added. + */ + + +int addprotocolsfromservmask(char *mask, u8 *porttbl, struct scan_lists *ports) { + struct protocol_list *current; + int bucket, t=0; + + if (!protocols_initialized && nmap_protocols_init() == -1) + fatal("addprotocolsfromservmask: Couldn't get protocol numbers"); + + for(bucket = 0; bucket < PROTOCOL_TABLE_SIZE; bucket++) { + for(current = protocol_table[bucket % PROTOCOL_TABLE_SIZE]; current; current = current->next) { + if (wildtest(mask, current->protoent->p_name)) { + porttbl[ntohs(current->protoent->p_proto)] |= SCAN_PROTOCOLS; + ports->prot_count++; + t++; + } + } + } + + return t; + +} + + struct protoent *nmap_getprotbynum(int num) { struct protocol_list *current; - nmap_protocols_init(); + + if (nmap_protocols_init() == -1) + return NULL; + for(current = protocol_table[num % PROTOCOL_TABLE_SIZE]; current; current = current->next) { if (num == current->protoent->p_proto) @@ -189,61 +220,4 @@ struct protoent *nmap_getprotbynum(int num) { /* Couldn't find it ... oh well. */ return NULL; - } - -/* By default we do all prots 0-255. */ -struct scan_lists *getdefaultprots(void) { - int protindex = 0; - struct scan_lists *scanlist; - /*struct protocol_list *current;*/ - int bucket; - int protsneeded = 256; - - if (nmap_protocols_init() == -1) - fatal("getdefaultprots(): Couldn't get protocol numbers"); - - scanlist = (struct scan_lists *) safe_zalloc(sizeof(struct scan_lists)); - scanlist->prots = (unsigned short *) safe_zalloc((protsneeded) * sizeof(unsigned short)); - scanlist->prot_count = protsneeded; - - for(bucket = 0; bucket < protsneeded; bucket++) { - scanlist->prots[protindex++] = bucket; - } - return scanlist; -} - -struct scan_lists *getfastprots(void) { - int protindex = 0; - struct scan_lists *scanlist; - char usedprots[256]; - struct protocol_list *current; - int bucket; - int protsneeded = 0; - - if (nmap_protocols_init() == -1) - fatal("Getfastprots: Couldn't get protocol numbers"); - - memset(usedprots, 0, sizeof(usedprots)); - - for(bucket = 0; bucket < PROTOCOL_TABLE_SIZE; bucket++) { - for(current = protocol_table[bucket % PROTOCOL_TABLE_SIZE]; - current; current = current->next) { - if (!usedprots[ntohs(current->protoent->p_proto)]) - usedprots[ntohs(current->protoent->p_proto)] = 1; - protsneeded++; - } - } - - scanlist = (struct scan_lists *) safe_zalloc(sizeof(struct scan_lists)); - scanlist->prots = (unsigned short *) safe_zalloc((protsneeded ) * sizeof(unsigned short)); - scanlist->prot_count = protsneeded; - - for(bucket = 0; bucket < 256; bucket++) { - if (usedprots[bucket]) - scanlist->prots[protindex++] = bucket; - } - - return scanlist; -} - diff --git a/protocols.h b/protocols.h index 343638889..8ce5acfc9 100644 --- a/protocols.h +++ b/protocols.h @@ -119,9 +119,7 @@ struct protocol_list { struct protocol_list *next; }; +int addprotocolsfromservmask(char *mask, u8 *porttbl, struct scan_lists *ports); struct protoent *nmap_getprotbynum(int num); -struct scan_lists *getfastprots(void); -struct scan_lists *getdefaultprots(void); - #endif diff --git a/services.cc b/services.cc index a894b0faf..6cfa279ad 100644 --- a/services.cc +++ b/services.cc @@ -106,9 +106,11 @@ 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 int services_initialized = 0; +static int ratio_format = 0; // 0 = /etc/services no-ratio format. 1 = new nmap format static int nmap_services_init() { - static int services_initialized = 0; if (services_initialized) return 0; char filename[512]; @@ -118,8 +120,10 @@ static int nmap_services_init() { char *p; char line[1024]; int lineno = 0; - struct service_list *current, *previous; + struct service_list *current, *previous, *sp; int res; + double ratio; + int ratio_n, ratio_d; if (nmap_fetchfile(filename, sizeof(filename), "nmap-services") != 1) { #ifndef WIN32 @@ -159,12 +163,27 @@ static int nmap_services_init() { p++; if (*p == '#') continue; - res = sscanf(line, "%127s %hu/%15s", servicename, &portno, proto); - if (res !=3) + + res = sscanf(line, "%127s %hu/%15s %d/%d", servicename, &portno, proto, &ratio_n, &ratio_d); + + if (res == 3) { + ratio = 0; + } else if (res == 5) { + if (ratio_n > ratio_d) + fatal("%s:%d has a ratio %g. All ratios must be < 1", filename, lineno, (double)ratio_n/ratio_d); + + if (ratio_d == 0) + fatal("%s:%d has a ratio denominator of 0 causing a division by 0 error", filename, lineno); + + ratio = (double)ratio_n / ratio_d; + ratio_format = 1; + } else { continue; + } + portno = htons(portno); - /* Now we make sure our services doesn't have duplicates */ + /* 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 && @@ -198,6 +217,7 @@ static int nmap_services_init() { 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; @@ -208,13 +228,75 @@ static int nmap_services_init() { current->servent->s_port = portno; current->servent->s_proto = cp_strdup(proto); current->servent->s_aliases = NULL; + + sp = (struct service_list *) cp_alloc(sizeof(struct service_list)); + sp->servent = current->servent; + sp->ratio = current->ratio; + sp->next = NULL; + + 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; + } + } + } + fclose(fp); services_initialized = 1; return 0; } + +/* Adds ports whose names match mask and one or more protocols + * specified by range_type to porttbl. Increases the respective + * protocol counts in ports. + * Returns the number of ports added in total. + */ + +int addportsfromservmask(char *mask, u8 *porttbl, struct scan_lists *ports, int range_type) { + struct service_list *current; + int bucket,t=0; + + if (!services_initialized && nmap_services_init() == -1) + fatal("addportsfromservmask: Couldn't get port numbers"); + + 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; + ports->tcp_count++; + t++; + } + + if ((range_type & SCAN_UDP_PORT) && strcmp(current->servent->s_proto, "udp") == 0) { + porttbl[ntohs(current->servent->s_port)] |= SCAN_UDP_PORT; + ports->udp_count++; + t++; + } + + } + } + } + + return t; + +} + + + struct servent *nmap_getservbyport(int port, const char *proto) { struct service_list *current; @@ -222,7 +304,7 @@ struct servent *nmap_getservbyport(int port, const char *proto) { return NULL; for(current = service_table[port % SERVICE_TABLE_SIZE]; - current; current = current->next) { + current; current = current->next) { if (((u16) port == (u16) current->servent->s_port) && strcmp(proto, current->servent->s_proto) == 0) return current->servent; @@ -230,123 +312,152 @@ struct servent *nmap_getservbyport(int port, const char *proto) { /* Couldn't find it ... oh well. */ return NULL; - + } -/* Be default we do all ports 1-1024 as well as any higher ports - that are in the services file */ -struct scan_lists *getdefaultports(int tcpscan, int udpscan) { - int tcpportindex = 0; - int udpportindex = 0; - struct scan_lists *ports; - u8 *usedports; + + +static int port_compare(const void *a, const void *b) { + unsigned short ua = *((unsigned short *) a), ub = *((unsigned short *) b); + if (ua > ub) return 1; + else return -1; +} + + + +// is_port_member() returns true if serv is an element of ptsdata. +// This could be implemented MUCH more efficiently but it should only be +// 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) { + int i; + + if (serv->servent->s_proto[0] == 't') { + for (i=0; itcp_count; i++) + if (ntohs(serv->servent->s_port) == ptsdata->tcp_ports[i]) return 1; + } else { + for (i=0; iudp_count; i++) + if (ntohs(serv->servent->s_port) == ptsdata->udp_ports[i]) return 1; + } + + return 0; +} + +// gettoppts() returns a scan_list with the most common ports scanned by +// Nmap according to the ratios specified in the nmap-services file. +// +// If level is below 1.0 then we treat it as a minimum ratio and we +// add all ports with ratios above level. +// +// If level is 1 or above, we treat it as a "top ports" directive +// and return the N highest ratio ports (where N==level). +// +// This function doesn't support IP protocol scan so only call this +// function if o.TCPScan() || o.UDPScan() + +struct scan_lists *gettoppts(double level, char *portlist) { + int ti=0, ui=0; + struct scan_lists *sl, *ptsdata=NULL; struct service_list *current; - int bucket; - int tcpportsneeded = 0; - int udpportsneeded = 0; - if (nmap_services_init() == -1) - fatal("Getfastports: Couldn't get port numbers"); - - usedports = (u8 *) safe_zalloc(sizeof(*usedports) * 65536); + if (!services_initialized && nmap_services_init() == -1) + fatal("gettoppts: Couldn't get port numbers"); - for(bucket = 1; bucket < 1025; bucket++) { - if (tcpscan) { - usedports[bucket] |= SCAN_TCP_PORT; - tcpportsneeded++; - } - if (udpscan) { - usedports[bucket] |= SCAN_UDP_PORT; - udpportsneeded++; - } + if (ratio_format == 0) { + if (level != -1) + fatal("Unable to use --top-ports or --port-ratio with an old style (no-ratio) services file"); + + if (portlist) + return getpts(portlist); + else if (o.fastscan) + return getpts("[-]"); + else + return getpts("1-1024,[1025-]"); } - for(bucket = 0; bucket < SERVICE_TABLE_SIZE; bucket++) { - for(current = service_table[bucket % SERVICE_TABLE_SIZE]; - current; current = current->next) { - if (tcpscan && - ! (usedports[ntohs(current->servent->s_port)] & SCAN_TCP_PORT) && - ! strncmp(current->servent->s_proto, "tcp", 3)) { - usedports[ntohs(current->servent->s_port)] |= SCAN_TCP_PORT; - tcpportsneeded++; - } - if (udpscan && - ! (usedports[ntohs(current->servent->s_port)] & SCAN_UDP_PORT) && - !strncmp(current->servent->s_proto, "udp", 3)) { - usedports[ntohs(current->servent->s_port)] |= SCAN_UDP_PORT; - udpportsneeded++; - } + // TOP PORT DEFAULTS + if (level == -1) { + if (portlist) + return getpts(portlist); + + if (o.fastscan) level = 100; + else level = 0.01; + } + + sl = (struct scan_lists *) safe_zalloc(sizeof(struct scan_lists)); + if (portlist) ptsdata = getpts(portlist); + + if (level < 1) { + for (current=sorted_services; current; current=current->next) { + if (ptsdata && !is_port_member(ptsdata, current)) continue; + + if (current->ratio >= level) { + if (o.TCPScan() && current->servent->s_proto[0] == 't') sl->tcp_count++; + else if (o.UDPScan() && current->servent->s_proto[0] == 'u') sl->udp_count++; + } else break; } - } - ports = (struct scan_lists *) safe_zalloc(sizeof(struct scan_lists)); - if (tcpscan) - ports->tcp_ports = (unsigned short *) safe_zalloc((tcpportsneeded) * sizeof(unsigned short)); - if (udpscan) - ports->udp_ports = (unsigned short *) safe_zalloc((udpportsneeded) * sizeof(unsigned short)); - ports->tcp_count= tcpportsneeded; - ports->udp_count= udpportsneeded; + if (sl->tcp_count) + sl->tcp_ports = (unsigned short *)safe_zalloc(sl->tcp_count * sizeof(unsigned short)); - for(bucket = 0; bucket < 65536; bucket++) { - if (usedports[bucket] & SCAN_TCP_PORT) - ports->tcp_ports[tcpportindex++] = bucket; - if (usedports[bucket] & SCAN_UDP_PORT) - ports->udp_ports[udpportindex++] = bucket; - } + if (sl->udp_count) + sl->udp_ports = (unsigned short *)safe_zalloc(sl->udp_count * sizeof(unsigned short)); - free(usedports); - return ports; + sl->prots = NULL; + + for (current=sorted_services;current;current=current->next) { + if (ptsdata && !is_port_member(ptsdata, current)) continue; + + if (current->ratio >= level) { + if (o.TCPScan() && current->servent->s_proto[0] == 't') + sl->tcp_ports[ti++] = ntohs(current->servent->s_port); + else if (o.UDPScan() && current->servent->s_proto[0] == 'u') + sl->udp_ports[ui++] = ntohs(current->servent->s_port); + } else break; + } + } else if (level >= 1) { + if (level > 65536) + fatal("Level argument to gettoppts (%g) is too large", level); + + if (o.TCPScan()) { + sl->tcp_count = MIN((int) level, numtcpports); + sl->tcp_ports = (unsigned short *)safe_zalloc(sl->tcp_count * sizeof(unsigned short)); + } + + if (o.UDPScan()) { + sl->udp_count = MIN((int) level, numudpports); + sl->udp_ports = (unsigned short *)safe_zalloc(sl->udp_count * sizeof(unsigned short)); + } + + sl->prots = NULL; + + for (current=sorted_services;current && (ti < sl->tcp_count || ui < sl->udp_count);current=current->next) { + if (ptsdata && !is_port_member(ptsdata, current)) continue; + + if (o.TCPScan() && current->servent->s_proto[0] == 't' && ti < sl->tcp_count) + sl->tcp_ports[ti++] = ntohs(current->servent->s_port); + else if (o.UDPScan() && current->servent->s_proto[0] == 'u' && ui < sl->udp_count) + sl->udp_ports[ui++] = ntohs(current->servent->s_port); + } + + if (ti < sl->tcp_count) sl->tcp_count = ti; + if (ui < sl->udp_count) sl->udp_count = ui; + } else + fatal("Argument to gettoppts (%g) should be a positive ratio below 1 or an integer of 1 or higher", level); + + if (ptsdata) free_scan_lists(ptsdata); + + if (sl->tcp_count > 1) + qsort(sl->tcp_ports, sl->tcp_count, sizeof(unsigned short), &port_compare); + + if (sl->udp_count > 1) + qsort(sl->udp_ports, sl->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, sl->tcp_count, sl->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, sl->tcp_count, sl->udp_count); + + return sl; } - -struct scan_lists *getfastports(int tcpscan, int udpscan) { - int tcpportindex = 0; - int udpportindex = 0; - struct scan_lists *ports; - u8 *usedports; - struct service_list *current; - int bucket; - int tcpportsneeded = 0; - int udpportsneeded = 0; - - if (nmap_services_init() == -1) - fatal("Getfastports: Couldn't get port numbers"); - - usedports = (u8 *) safe_zalloc(sizeof(*usedports) * 65536); - - for(bucket = 0; bucket < SERVICE_TABLE_SIZE; bucket++) { - for(current = service_table[bucket % SERVICE_TABLE_SIZE]; - current; current = current->next) { - if (tcpscan && - ! (usedports[ntohs(current->servent->s_port)] & SCAN_TCP_PORT) && - !strncmp(current->servent->s_proto, "tcp", 3)) { - usedports[ntohs(current->servent->s_port)] |= SCAN_TCP_PORT; - tcpportsneeded++; - } - if (udpscan && - ! (usedports[ntohs(current->servent->s_port)] & SCAN_UDP_PORT) && - !strncmp(current->servent->s_proto, "udp", 3)) { - usedports[ntohs(current->servent->s_port)] |= SCAN_UDP_PORT; - udpportsneeded++; - } - } - } - - ports = (struct scan_lists *) safe_zalloc(sizeof(struct scan_lists)); - if (tcpscan) - ports->tcp_ports = (unsigned short *) safe_zalloc((tcpportsneeded) * sizeof(unsigned short)); - if (udpscan) - ports->udp_ports = (unsigned short *) safe_zalloc((udpportsneeded) * sizeof(unsigned short)); - ports->tcp_count= tcpportsneeded; - ports->udp_count= udpportsneeded; - - for(bucket = 0; bucket < 65536; bucket++) { - if (usedports[bucket] & SCAN_TCP_PORT) - ports->tcp_ports[tcpportindex++] = bucket; - if (usedports[bucket] & SCAN_UDP_PORT) - ports->udp_ports[udpportindex++] = bucket; - } - - free(usedports); - return ports; -} - diff --git a/services.h b/services.h index 1561058d8..8b7f291d0 100644 --- a/services.h +++ b/services.h @@ -125,11 +125,12 @@ struct service_list { struct servent *servent; + double ratio; struct service_list *next; }; +int addportsfromservmask(char *mask, u8 *porttbl, struct scan_lists *ports, int range_type); struct servent *nmap_getservbyport(int port, const char *proto); -struct scan_lists *getfastports(int tcpscan, int udpscan); -struct scan_lists *getdefaultports(int tcpscan, int udpscan); +struct scan_lists *gettoppts(double level, char *portlist); #endif diff --git a/utils.cc b/utils.cc index 2000398c5..ccae7cfec 100644 --- a/utils.cc +++ b/utils.cc @@ -105,6 +105,49 @@ extern NmapOps o; + + +/* Test a wildcard mask against a test string. Wildcard mask + * can include '*' and '?' which work the same as they do + * in /bin/sh (except it's case insensitive) + * Return val of 1 means it DID match. 0 means it DIDN'T + * - Doug Hoyte, 2005 + */ + +int wildtest(char *wild, char *test) { + + int i; + + while(*wild != '\0' || *test != '\0') { + if (*wild == '*') { + + /* --- Deal with multiple asterisks. --- */ + while (wild[1] == '*') wild++; + + /* --- Deal with terminating asterisks. --- */ + if (wild[1] == '\0') return 1; + + for(i=0; test[i]!='\0'; i++) + if ((wild[1] == test[i] || wild[1] == '?') + && wildtest(wild+1, test+i) == 1) return 1; + + return 0; + } + + /* --- '?' can't match '\0'. --- */ + if (*wild == '?' && *test == '\0') return 0; + + if (*wild != '?' && tolower((int)*wild) != tolower((int)*test)) return 0; + wild++; test++; + } + + if (tolower((int)*wild) == tolower((int)*test)) return 1; + return 0; + +} + + + /* Hex dump */ void hdump(unsigned char *packet, unsigned int len) { unsigned int i=0, j=0; diff --git a/utils.h b/utils.h index 172792edb..0ec8a6831 100644 --- a/utils.h +++ b/utils.h @@ -189,6 +189,8 @@ template T box(T bmin, T bmax, T bnum) { return bnum; } +int wildtest(char *wild, char *test); + void hdump(unsigned char *packet, unsigned int len); void lamont_hdump(char *cp, unsigned int length);