#include "read-service-probes.h" #include "util-malloc.h" #include "massip-port.h" #include "unusedparm.h" #include #include #include #include #ifdef _MSC_VER #pragma warning(disable:4996) #endif #if defined(WIN32) #define strncasecmp _strnicmp #endif /***************************************************************************** * Translate string name into enumerated type *****************************************************************************/ static enum SvcP_RecordType parse_type(const char *line, size_t *r_offset, size_t line_length) { static const struct { const char *name; size_t length; enum SvcP_RecordType type; } name_to_types[] = { {"exclude", 7, SvcP_Exclude}, {"probe", 5, SvcP_Probe}, {"match", 5, SvcP_Match}, {"softmatch", 9, SvcP_Softmatch}, {"ports", 5, SvcP_Ports}, {"sslports", 8, SvcP_Sslports}, {"totalwaitms", 11, SvcP_Totalwaitms}, {"tcpwrappedms",12, SvcP_Tcpwrappedms}, {"rarity", 6, SvcP_Rarity}, {"fallback", 8, SvcP_Fallback}, {0, SvcP_Unknown} }; size_t i; size_t offset = *r_offset; size_t name_length; size_t name_offset; enum SvcP_RecordType result; /* find length of command name */ name_offset = offset; while (offset < line_length && !isspace(line[offset])) offset++; /* name = all non-space chars until first space */ name_length = offset - name_offset; while (offset < line_length && isspace(line[offset])) offset++; /* trim whitespace after name */ *r_offset = offset; /* Lookup the command name */ for (i=0; name_to_types[i].name; i++) { if (name_length != name_to_types[i].length) continue; if (strncasecmp(line+name_offset, name_to_types[i].name, name_length) == 0) { break; } } result = name_to_types[i].type; /* return the type */ return result; } /***************************************************************************** *****************************************************************************/ static int is_hexchar(int c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return 1; default: return 0; } } /***************************************************************************** *****************************************************************************/ static unsigned hexval(int c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return c - '0'; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return c - 'a' + 10; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return c - 'A' + 10; default: return (unsigned)~0; } } /***************************************************************************** *****************************************************************************/ static struct RangeList parse_ports(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: Exclude 53,T:9100,U:30000-40000 ports 21,43,110,113,199,505,540,1248,5432,30444 ports 111,4045,32750-32810,38978 sslports 443 */ unsigned is_error = 0; const char *p; struct RangeList ranges = {0}; UNUSEDPARM(line_length); p = rangelist_parse_ports(&ranges, line + offset, &is_error, 0); if (is_error) { fprintf(stderr, "%s:%u:%u: bad port spec\n", list->filename, list->line_number, (unsigned)(p-line)); rangelist_remove_all(&ranges); } return ranges; } /***************************************************************************** *****************************************************************************/ static unsigned parse_number(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: totalwaitms 6000 tcpwrappedms 3000 rarity 6 */ unsigned number = 0; while (offset < line_length && isdigit(line[offset])) { number = number * 10; number = number + (line[offset] - '0'); offset++; } while (offset < line_length && isspace(line[offset])) offset++; if (offset != line_length) { fprintf(stderr, "%s:%u:%u: unexpected character '%c'\n", list->filename, list->line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); } return number; } /***************************************************************************** *****************************************************************************/ static char * parse_name(const char *line, size_t *r_offset, size_t line_length) { size_t name_offset = *r_offset; size_t name_length; char *result; /* grab all characters until first space */ while (*r_offset < line_length && !isspace(line[*r_offset])) (*r_offset)++; name_length = *r_offset - name_offset; if (name_length == 0) return 0; /* trim trailing white space */ while (*r_offset < line_length && isspace(line[*r_offset])) (*r_offset)++; /* allocate result string */ result = MALLOC(name_length+1); memcpy(result, line + name_offset, name_length+1); result[name_length] = '\0'; return result; } /***************************************************************************** *****************************************************************************/ static struct ServiceProbeFallback * parse_fallback(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: fallback GetRequest,GenericLines */ struct ServiceProbeFallback *result = 0; while (offset < line_length) { size_t name_offset; size_t name_length; struct ServiceProbeFallback *fallback; struct ServiceProbeFallback **r_fallback; /* grab all characters until first space */ name_offset = offset; while (offset < line_length && !isspace(line[offset]) && line[offset] != ',') offset++; name_length = offset - name_offset; while (offset < line_length && (isspace(line[offset]) || line[offset] == ',')) offset++; /* trim trailing whitespace */ if (name_length == 0) { fprintf(stderr, "%s:%u:%u: name too short\n", list->filename, list->line_number, (unsigned)name_offset); break; } /* Allocate a record */ fallback = CALLOC(1, sizeof(*fallback)); fallback->name = MALLOC(name_length+1); memcpy(fallback->name, line+name_offset, name_length+1); fallback->name[name_length] = '\0'; /* append to end of list */ for (r_fallback=&result; *r_fallback; r_fallback = &(*r_fallback)->next) ; fallback->next = *r_fallback; *r_fallback = fallback; } return result; } /***************************************************************************** *****************************************************************************/ static void parse_probe(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: Probe TCP GetRequest q|GET / HTTP/1.0\r\n\r\n| Probe UDP DNSStatusRequest q|\0\0\x10\0\0\0\0\0\0\0\0\0| Probe TCP NULL q|| */ const char *filename = list->filename; unsigned line_number = list->line_number; struct NmapServiceProbe *probe; /* * We have a new 'Probe', so append a blank record to the end of * our list */ probe = CALLOC(1, sizeof(*probe)); if (list->count + 1 >= list->max) { list->max = list->max * 2 + 1; list->list = REALLOCARRAY(list->list, sizeof(list->list[0]), list->max); } list->list[list->count++] = probe; /* * */ if (line_length - offset <= 3) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } if (memcmp(line+offset, "TCP", 3) == 0) probe->protocol = 6; else if (memcmp(line+offset, "UDP", 3) == 0) probe->protocol = 17; else { fprintf(stderr, "%s:%u:%u: unknown protocol\n", filename, line_number, (unsigned)offset); goto parse_error; } offset += 3; if (!isspace(line[offset])) { fprintf(stderr, "%s:%u:%u: unexpected character\n", filename, line_number, (unsigned)offset); goto parse_error; } while (offset < line_length && isspace(line[offset])) offset++; /* * */ probe->name = parse_name(line, &offset, line_length); if (probe->name == 0) { fprintf(stderr, "%s:%u:%u: probename parse error\n", filename, line_number, (unsigned)offset); goto parse_error; } /* * * - must start with a 'q' character * - a delimiter character starts/stop the string, typically '|' * - Traditional C-style escapes work: * \\ \0, \a, \b, \f, \n, \r, \t, \v, and \xXX */ { char delimiter; char *x; size_t x_offset; if (line_length - offset <= 2) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } if (line[offset++] != 'q') { fprintf(stderr, "%s:%u:%u: expected 'q', found '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset-1])?line[offset-1]:'.'); goto parse_error; } /* The next character is a 'delimiter' that starts and stops the next * string of characters, it it usually '|' but may be anything, like '/', * as long as the delimiter itself is not contained inside the string */ delimiter = line[offset++]; /* allocate a buffer at least as long as the remainder of the line. This is * probably too large, but cannot be too small. It's okay if we waste a * few characters. */ x = CALLOC(1, line_length - offset + 1); probe->hellostring = x; /* Grab all the characters until the next delimiter, translating escaped * characters as needed */ x_offset = 0; while (offset < line_length && line[offset] != delimiter) { /* Normal case: unescaped characters */ if (line[offset] != '\\') { x[x_offset++] = line[offset++]; continue; } /* skip escape character '\\' */ offset++; if (offset >= line_length || line[offset] == delimiter) { fprintf(stderr, "%s:%u:%u: premature end of field\n", filename, line_number, (unsigned)offset); goto parse_error; } /* Handled escape sequence */ switch (line[offset++]) { default: fprintf(stderr, "%s:%u: %.*s\n", filename, line_number, (unsigned)line_length, line); fprintf(stderr, "%s:%u:%u: unexpected escape character '%c'\n", filename, line_number, (unsigned)offset-1, isprint(line[offset-1])?line[offset-1]:'.'); goto parse_error; case '\\': x[x_offset++] = '\\'; break; case '0': x[x_offset++] = '\0'; break; case 'a': x[x_offset++] = '\a'; break; case 'b': x[x_offset++] = '\b'; break; case 'f': x[x_offset++] = '\f'; break; case 'n': x[x_offset++] = '\n'; break; case 'r': x[x_offset++] = '\r'; break; case 't': x[x_offset++] = '\t'; break; case 'v': x[x_offset++] = '\v'; break; case 'x': /* make sure at least 2 characters exist in input, either due * to line-length or the delimiter */ if (offset + 2 >= line_length || line[offset+0] == delimiter || line[offset+1] == delimiter) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } /* make sure those two characters are hex digits */ if (!is_hexchar(line[offset+0]) || !is_hexchar(line[offset+1])) { fprintf(stderr, "%s:%u:%u: expected hex, found '%c%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset+1])?line[offset+1]:'.', isprint(line[offset+2])?line[offset+2]:'.' ); goto parse_error; } /* parse those two hex digits */ x[x_offset++] = (char)(hexval(line[offset+0])<< 4 | hexval(line[offset+1])); offset += 2; break; } } probe->hellolength = x_offset; if (offset >= line_length || line[offset] != delimiter) { fprintf(stderr, "%s:%u:%u: missing end delimiter '%c'\n", filename, line_number, (unsigned)offset, isprint(delimiter)?delimiter:'.'); goto parse_error; } //offset++; } return; parse_error: if (probe->name != 0) free(probe->name); if (probe->hellostring != 0) free(probe->hellostring); probe->hellostring = 0; free(probe); list->count--; } /***************************************************************************** *****************************************************************************/ static struct ServiceProbeMatch * parse_match(struct NmapServiceProbeList *list, const char *line, size_t offset, size_t line_length) { /* Examples: match ftp m/^220.*Welcome to .*Pure-?FTPd (\d\S+\s*)/ p/Pure-FTPd/ v/$1/ cpe:/a:pureftpd:pure-ftpd:$1/ match ssh m/^SSH-([\d.]+)-OpenSSH[_-]([\w.]+)\r?\n/i p/OpenSSH/ v/$2/ i/protocol $1/ cpe:/a:openbsd:openssh:$2/ match mysql m|^\x10\0\0\x01\xff\x13\x04Bad handshake$| p/MySQL/ cpe:/a:mysql:mysql/ match chargen m|@ABCDEFGHIJKLMNOPQRSTUVWXYZ| match uucp m|^login: login: login: $| p/NetBSD uucpd/ o/NetBSD/ cpe:/o:netbsd:netbsd/a match printer m|^([\w-_.]+): lpd: Illegal service request\n$| p/lpd/ h/$1/ match afs m|^[\d\D]{28}\s*(OpenAFS)([\d\.]{3}[^\s\0]*)\0| p/$1/ v/$2/ */ const char *filename = list->filename; unsigned line_number = list->line_number; struct ServiceProbeMatch *match; match = CALLOC(1, sizeof(*match)); /* * */ match->service = parse_name(line, &offset, line_length); if (match->service == 0) { fprintf(stderr, "%s:%u:%u: servicename is empty\n", filename, line_number, (unsigned)offset); goto parse_error; } /* * * - must start with a 'm' character * - a delimiter character starts/stop the string, typically '/' or '|' * - contents are PCRE regex */ { char delimiter; size_t regex_offset; size_t regex_length; /* line must start with 'm' */ if (line_length - offset <= 2) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } if (line[offset] != 'm') { fprintf(stderr, "%s:%u:%u: expected 'm', found '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); goto parse_error; } offset++; /* next character is the delimiter */ delimiter = line[offset++]; /* Find the length of the regex */ regex_offset = offset; while (offset < line_length && line[offset] != delimiter) offset++; regex_length = offset - regex_offset; if (offset >= line_length || line[offset] != delimiter) { fprintf(stderr, "%s:%u:%u: missing ending delimiter '%c'\n", filename, line_number, (unsigned)offset, isprint(delimiter)?delimiter:'.'); goto parse_error; } else offset++; /* add regex pattern to record */ match->regex_length = regex_length; match->regex = MALLOC(regex_length + 1); memcpy(match->regex, line+regex_offset, regex_length + 1); match->regex[regex_length] = '\0'; /* Verify the regex options characters */ while (offsetis_case_insensitive = 1; break; case 's': match->is_include_newlines = 1; break; default: fprintf(stderr, "%s:%u:%u: unknown regex pattern option '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); goto parse_error; } offset++; } while (offset * - several optional fields * - each file starts with identifier (p v i h o d cpe:) * - next comes the delimiter character (preferably '/' slash) * - next comes data * - ends with delimiter */ while (offset < line_length) { char id; char delimiter; size_t value_length; size_t value_offset; int is_a = 0; enum SvcV_InfoType type; /* Make sure we have enough characters for a versioninfo string */ if (offset >= line_length) break; if (offset + 2 >= line_length) { fprintf(stderr, "%s:%u:%u: unexpected character at end of line '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); goto parse_error; } /* grab the 'id' character, which is either singe letter or the string 'cpe:' */ id = line[offset++]; if (id == 'c') { if (offset + 3 >= line_length) { fprintf(stderr, "%s:%u:%u: unexpected character at end of line '%c'\n", filename, line_number, (unsigned)offset, isprint(line[offset])?line[offset]:'.'); goto parse_error; } if (memcmp(line+offset, "pe:", 3) != 0) { fprintf(stderr, "%s:%u:%u: expected string 'cpe:'\n", filename, line_number, (unsigned)offset); goto parse_error; } offset += 3; } switch (id) { case 'p': type = SvcV_ProductName; break; case 'v': type = SvcV_Version; break; case 'i': type = SvcV_Info; break; case 'h': type = SvcV_Hostname; break; case 'o': type = SvcV_OperatingSystem; break; case 'd': type = SvcV_DeviceType; break; case 'c': type = SvcV_CpeName; break; default: fprintf(stderr, "%s:%u:%u: versioninfo unknown identifier '%c'\n", filename, line_number, (unsigned)offset, isprint(id)?id:'.'); goto parse_error; } /* grab the delimiter */ if (offset + 2 >= line_length) { fprintf(stderr, "%s:%u:%u: line too short\n", filename, line_number, (unsigned)offset); goto parse_error; } delimiter = line[offset++]; /* Grab the contents of this string */ value_offset = offset; while (offset < line_length && line[offset] != delimiter) offset++; value_length = offset - value_offset; if (offset >= line_length || line[offset] != delimiter) { fprintf(stderr, "%s:%u:%u: missing ending delimiter '%c'\n", filename, line_number, (unsigned)offset, isprint(delimiter)?delimiter:'.'); goto parse_error; } else offset++; if (id == 'c' && offset + 1 <= line_length && line[offset] == 'a') { is_a = 1; offset++; } if (offset < line_length && !isspace(line[offset])) { fprintf(stderr, "%s:%u:%u: unexpected character after delimiter '%c'\n", filename, line_number, (unsigned)offset, isprint(delimiter)?delimiter:'.'); goto parse_error; } while (offset < line_length && isspace(line[offset])) offset++; /* Create a versioninfo record */ { struct ServiceVersionInfo *v; struct ServiceVersionInfo **r_v; v = CALLOC(1, sizeof(*v)); v->type = type; v->value = MALLOC(value_length + 1); memcpy(v->value, line+value_offset, value_length+1); v->value[value_length] = '\0'; v->is_a = is_a; /* insert at end of list */ for (r_v = &match->versioninfo; *r_v; r_v = &(*r_v)->next) ; v->next = *r_v; *r_v = v; } } return match; parse_error: free(match->regex); free(match->service); while (match->versioninfo) { struct ServiceVersionInfo *v = match->versioninfo; match->versioninfo = v->next; if (v->value) free(v->value); free(v); } free(match); return 0; } /***************************************************************************** *****************************************************************************/ static void parse_line(struct NmapServiceProbeList *list, const char *line) { const char *filename = list->filename; unsigned line_number = list->line_number; size_t line_length; size_t offset; enum SvcP_RecordType type; struct RangeList ranges = {0}; struct NmapServiceProbe *probe; /* trim whitespace */ offset = 0; line_length = strlen(line); while (offset && isspace(line[offset])) offset++; while (line_length && isspace(line[line_length-1])) line_length--; /* Ignore comment lines */ if (ispunct(line[offset])) return; /* Ignore empty lines */ if (offset >= line_length) return; /* parse the type field field */ type = parse_type(line, &offset, line_length); /* parse the remainder of the line, depending upon the type */ switch ((int)type) { case SvcP_Unknown: fprintf(stderr, "%s:%u:%u: unknown type: '%.*s'\n", filename, line_number, (unsigned)offset, (int)offset-0, line); return; case SvcP_Exclude: if (list->count) { /* The 'Exclude' directive is only valid at the top of the file, * before any Probes */ fprintf(stderr, "%s:%u:%u: 'Exclude' directive only valid before any 'Probe'\n", filename, line_number, (unsigned)offset); } else { ranges = parse_ports(list, line, offset, line_length); if (ranges.count == 0) { fprintf(stderr, "%s:%u:%u: 'Exclude' bad format\n", filename, line_number, (unsigned)offset); } else { rangelist_merge(&list->exclude, &ranges); rangelist_remove_all(&ranges); } } return; case SvcP_Probe: /* Creates a new probe record, all the other types (except 'Exclude') operate * on the current probe record */ parse_probe(list, line, offset, line_length); return; } /* * The remaining items only work in the context of the current 'Probe' * directive */ if (list->count == 0) { fprintf(stderr, "%s:%u:%u: 'directive only valid after a 'Probe'\n", filename, line_number, (unsigned)offset); return; } probe = list->list[list->count-1]; switch ((int)type) { case SvcP_Ports: ranges = parse_ports(list, line, offset, line_length); if (ranges.count == 0) { fprintf(stderr, "%s:%u:%u: bad ports format\n", filename, line_number, (unsigned)offset); } else { rangelist_merge(&probe->ports, &ranges); rangelist_remove_all(&ranges); } break; case SvcP_Sslports: ranges = parse_ports(list, line, offset, line_length); if (ranges.count == 0) { fprintf(stderr, "%s:%u:%u: bad ports format\n", filename, line_number, (unsigned)offset); } else { rangelist_merge(&probe->sslports, &ranges); rangelist_remove_all(&ranges); } break; case SvcP_Match: case SvcP_Softmatch: { struct ServiceProbeMatch *match; match = parse_match(list, line, offset, line_length); if (match) { struct ServiceProbeMatch **r_match; /* put at end of list */ for (r_match = &probe->match; *r_match; r_match = &(*r_match)->next) ; match->next = *r_match; *r_match = match; match->is_softmatch = (type == SvcP_Softmatch); } } break; case SvcP_Totalwaitms: probe->totalwaitms = parse_number(list, line, offset, line_length); break; case SvcP_Tcpwrappedms: probe->tcpwrappedms = parse_number(list, line, offset, line_length); break; case SvcP_Rarity: probe->rarity = parse_number(list, line, offset, line_length); break; case SvcP_Fallback: { struct ServiceProbeFallback *fallback; fallback = parse_fallback(list, line, offset, line_length); if (fallback) { fallback->next = probe->fallback; probe->fallback = fallback; } } break; } } /***************************************************************************** *****************************************************************************/ static struct NmapServiceProbeList * nmapserviceprobes_new(const char *filename) { struct NmapServiceProbeList *result; result = CALLOC(1, sizeof(*result)); result->filename = filename; return result; } /***************************************************************************** *****************************************************************************/ struct NmapServiceProbeList * nmapserviceprobes_read_file(const char *filename) { FILE *fp; char line[32768]; struct NmapServiceProbeList *result; /* * Open the file */ fp = fopen(filename, "rt"); if (fp == NULL) { perror(filename); return 0; } /* * Create the result structure */ result = nmapserviceprobes_new(filename); /* * parse all lines in the text file */ while (fgets(line, sizeof(line), fp)) { /* Track line number for error messages */ result->line_number++; /* Parse this string into a record */ parse_line(result, line); } fclose(fp); result->filename = 0; /* name no longer valid after this point */ result->line_number = (unsigned)~0; /* line number no longer valid after this point */ nmapserviceprobes_print(result, stdout); return result; } /***************************************************************************** *****************************************************************************/ static void nmapserviceprobes_free_record(struct NmapServiceProbe *probe) { if (probe->name) free(probe->name); if (probe->hellostring) free(probe->hellostring); rangelist_remove_all(&probe->ports); rangelist_remove_all(&probe->sslports); while (probe->match) { struct ServiceProbeMatch *match = probe->match; probe->match = match->next; free(match->regex); free(match->service); while (match->versioninfo) { struct ServiceVersionInfo *v = match->versioninfo; match->versioninfo = v->next; if (v->value) free(v->value); free(v); } free(match); } while (probe->fallback) { struct ServiceProbeFallback *fallback; fallback = probe->fallback; probe->fallback = fallback->next; if (fallback->name) free(fallback->name); free(fallback); } free(probe); } /***************************************************************************** *****************************************************************************/ static void nmapserviceprobes_print_ports(const struct RangeList *ranges, FILE *fp, const char *prefix, int default_proto) { unsigned i; /* don't print anything if no ports */ if (ranges == NULL || ranges->count == 0) return; /* 'Exclude', 'ports', 'sslports' */ fprintf(fp, "%s ", prefix); /* print all ports */ for (i=0; icount; i++) { int proto; int begin = ranges->list[i].begin; int end = ranges->list[i].end; if (Templ_TCP <= begin && begin < Templ_UDP) proto = Templ_TCP; else if (Templ_UDP <= begin && begin < Templ_SCTP) proto = Templ_UDP; else proto = Templ_SCTP; /* If UDP, shift down */ begin -= proto; end -= proto; /* print comma between ports, but not for first port */ if (i) fprintf(fp, ","); /* Print either one number for a single port, or two numbers for a range */ if (default_proto != proto) { default_proto = proto; switch (proto) { case Templ_TCP: fprintf(fp, "T:"); break; case Templ_UDP: fprintf(fp, "U:"); break; case Templ_SCTP: fprintf(fp, "S"); break; case Templ_ICMP_echo: fprintf(fp, "e"); break; case Templ_ICMP_timestamp: fprintf(fp, "t"); break; case Templ_ARP: fprintf(fp, "A"); break; case Templ_VulnCheck: fprintf(fp, "v"); break; } } fprintf(fp, "%u", begin); if (end > begin) fprintf(fp, "-%u", end); } fprintf(fp, "\n"); } /***************************************************************************** *****************************************************************************/ static int contains_char(const char *string, size_t length, int c) { size_t i; for (i=0; iexclude, fp, "Exclude", ~0); for (i=0; icount; i++) { struct NmapServiceProbe *probe = list->list[i]; struct ServiceProbeMatch *match; /* print the first part of the probe */ fprintf(fp, "Probe %s %s q", (probe->protocol==6)?"TCP":"UDP", probe->name); /* print the query/hello string */ nmapserviceprobes_print_hello(fp, probe->hellostring, probe->hellolength, '|'); fprintf(fp, "\n"); if (probe->rarity) fprintf(fp, "rarity %u\n", probe->rarity); if (probe->totalwaitms) fprintf(fp, "totalwaitms %u\n", probe->totalwaitms); if (probe->tcpwrappedms) fprintf(fp, "tcpwrappedms %u\n", probe->tcpwrappedms); nmapserviceprobes_print_ports(&probe->ports, fp, "ports", (probe->protocol==6)?Templ_TCP:Templ_UDP); nmapserviceprobes_print_ports(&probe->sslports, fp, "sslports", (probe->protocol==6)?Templ_TCP:Templ_UDP); for (match=probe->match; match; match = match->next) { struct ServiceVersionInfo *vi; fprintf(fp, "match %s m", match->service); nmapserviceprobes_print_dstring(fp, match->regex, match->regex_length, '/'); if (match->is_case_insensitive) fprintf(fp, "i"); if (match->is_include_newlines) fprintf(fp, "s"); fprintf(fp, " "); for (vi=match->versioninfo; vi; vi=vi->next) { const char *tag; switch (vi->type) { case SvcV_Unknown: tag = "u"; break; case SvcV_ProductName: tag = "p"; break; case SvcV_Version: tag = "v"; break; case SvcV_Info: tag = "i"; break; case SvcV_Hostname: tag = "h"; break; case SvcV_OperatingSystem: tag = "o"; break; case SvcV_DeviceType: tag = "e"; break; case SvcV_CpeName: tag = "cpe:"; break; default: tag = ""; } fprintf(fp, "%s", tag); nmapserviceprobes_print_dstring(fp, vi->value, strlen(vi->value), '/'); if (vi->is_a) fprintf(fp, "a"); fprintf(fp, " "); } fprintf(fp, "\n"); } } } /***************************************************************************** *****************************************************************************/ void nmapserviceprobes_free(struct NmapServiceProbeList *list) { unsigned i; if (list == NULL) return; for (i=0; list->count; i++) { nmapserviceprobes_free_record(list->list[i]); } if (list->list) free(list->list); free(list); } /***************************************************************************** *****************************************************************************/ int nmapserviceprobes_selftest(void) { const char *lines[] = { "Exclude 53,T:9100,U:30000-40000\n", "Probe UDP DNSStatusRequest q|\\0\\0\\x10\\0\\0\\0\\0\\0\\0\\0\\0\\0|\n", "Probe TCP GetRequest q|GET / HTTP/1.0\r\n\r\n|\n", "ports 80\n", "sslports 443\n", "Probe TCP NULL q||\n", "ports 21,43,110,113,199,505,540,1248,5432,30444\n", "match ftp m/^220.*Welcome to .*Pure-?FTPd (\\d\\S+\\s*)/ p/Pure-FTPd/ v/$1/ cpe:/a:pureftpd:pure-ftpd:$1/\n", "match ssh m/^SSH-([\\d.]+)-OpenSSH[_-]([\\w.]+)\\r?\\n/i p/OpenSSH/ v/$2/ i/protocol $1/ cpe:/a:openbsd:openssh:$2/\n", "match mysql m|^\\x10\\0\\0\\x01\\xff\\x13\\x04Bad handshake$| p/MySQL/ cpe:/a:mysql:mysql/\n", "match chargen m|@ABCDEFGHIJKLMNOPQRSTUVWXYZ|\n", "match uucp m|^login: login: login: $| p/NetBSD uucpd/ o/NetBSD/ cpe:/o:netbsd:netbsd/a\n", "match printer m|^([\\w-_.]+): lpd: Illegal service request\\n$| p/lpd/ h/$1/\n", "match afs m|^[\\d\\D]{28}\\s*(OpenAFS)([\\d\\.]{3}[^\\s\\0]*)\\0| p/$1/ v/$2/\n", 0 }; unsigned i; struct NmapServiceProbeList *list = nmapserviceprobes_new(""); for (i=0; lines[i]; i++) { list->line_number = i; parse_line(list, lines[i]); } //nmapserviceprobes_print(list, stdout); return 0; }