diff -urNb nmap-3.81/nmap.cc nmap-3.81-p-switch-additions/nmap.cc --- nmap-3.81/nmap.cc 2005-02-07 00:52:52.000000000 -0800 +++ nmap-3.81-p-switch-additions/nmap.cc 2005-06-07 20:57:17.178646832 -0700 @@ -1364,26 +1364,126 @@ } } -/* 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 = SCAN_TCP_PORT|SCAN_UDP_PORT|SCAN_PROTOCOLS; + int portwarning = 0; + int i, tcpi, udpi, proti; 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; @@ -1399,7 +1499,28 @@ 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 = 1; } else if (isdigit((int) *current_range)) { @@ -1407,21 +1528,38 @@ 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 = 65535; } else if (isdigit((int) *current_range)) { @@ -1440,24 +1578,43 @@ /* 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 (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) - tcpportcount++; + ports->tcp_count++; if (range_type & SCAN_UDP_PORT) - udpportcount++; + ports->udp_count++; if (range_type & SCAN_PROTOCOLS && rangestart < 256) - protcount++; + 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-\""); } @@ -1465,39 +1622,9 @@ 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 printusage(char *name, int rc) { #ifdef WIN32 diff -urNb nmap-3.81/protocols.cc nmap-3.81-p-switch-additions/protocols.cc --- nmap-3.81/protocols.cc 2004-08-29 02:12:03.000000000 -0700 +++ nmap-3.81-p-switch-additions/protocols.cc 2005-06-07 20:57:47.580025120 -0700 @@ -176,6 +176,40 @@ } + + +/* 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) + if (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; diff -urNb nmap-3.81/protocols.h nmap-3.81-p-switch-additions/protocols.h --- nmap-3.81/protocols.h 2004-08-29 02:12:03.000000000 -0700 +++ nmap-3.81-p-switch-additions/protocols.h 2005-06-07 20:57:47.580025120 -0700 @@ -119,6 +119,7 @@ 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); diff -urNb nmap-3.81/services.cc nmap-3.81-p-switch-additions/services.cc --- nmap-3.81/services.cc 2004-08-29 02:12:03.000000000 -0700 +++ nmap-3.81-p-switch-additions/services.cc 2005-06-07 20:57:29.730738624 -0700 @@ -213,6 +213,49 @@ } + +/* 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) + if (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; diff -urNb nmap-3.81/services.h nmap-3.81-p-switch-additions/services.h --- nmap-3.81/services.h 2004-08-29 02:12:03.000000000 -0700 +++ nmap-3.81-p-switch-additions/services.h 2005-06-07 20:57:34.118071648 -0700 @@ -128,6 +128,7 @@ 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); diff -urNb nmap-3.81/utils.cc nmap-3.81-p-switch-additions/utils.cc --- nmap-3.81/utils.cc 2004-08-29 02:12:04.000000000 -0700 +++ nmap-3.81-p-switch-additions/utils.cc 2005-06-07 20:58:05.729266016 -0700 @@ -104,6 +104,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 -urNb nmap-3.81/utils.h nmap-3.81-p-switch-additions/utils.h --- nmap-3.81/utils.h 2004-08-29 02:12:04.000000000 -0700 +++ nmap-3.81-p-switch-additions/utils.h 2005-06-07 20:58:05.729266016 -0700 @@ -187,6 +187,8 @@ return bnum; } +int wildtest(char *wild, char *test); + void hdump(unsigned char *packet, unsigned int len); void lamont_hdump(char *cp, unsigned int length);