diff --git a/src/fieldset.c b/src/fieldset.c index 34c9fdf..eb5d27e 100644 --- a/src/fieldset.c +++ b/src/fieldset.c @@ -50,6 +50,26 @@ static inline void fs_add_word(fieldset_t *fs, const char *name, int type, f->free_ = free_; } +static void fs_modify_word(fieldset_t *fs, const char *name, int type, + int free_, size_t len, void *value) +{ + int i; + for (i=0; ilen; i++) { + if (!strcmp(fs->fields[i].name, name)) { + if (fs->fields[i].free_) { + free((void*)fs->fields[i].value); + fs->fields[i].value = 0; + } + fs->fields[i].type = type; + fs->fields[i].free_ = free_; + fs->fields[i].len = len; + fs->fields[i].value = (uint64_t)value; + return; + } + } + fs_add_word(fs, name, type, free_, len, value); +} + void fs_add_null(fieldset_t *fs, const char *name) { fs_add_word(fs, name, FS_NULL, 0, 0, NULL); @@ -71,6 +91,28 @@ void fs_add_binary(fieldset_t *fs, const char *name, size_t len, fs_add_word(fs, name, FS_BINARY, free_, len, value); } +// Modify +void fs_modify_null(fieldset_t *fs, const char *name) +{ + fs_modify_word(fs, name, FS_NULL, 0, 0, NULL); +} + +void fs_modify_string(fieldset_t *fs, const char *name, char *value, int free_) +{ + fs_modify_word(fs, name, FS_STRING, free_, strlen(value), (void*) value); +} + +void fs_modify_uint64(fieldset_t *fs, const char *name, uint64_t value) +{ + fs_modify_word(fs, name, FS_UINT64, 0, sizeof(uint64_t), (void*) value); +} + +void fs_modify_binary(fieldset_t *fs, const char *name, size_t len, + void *value, int free_) +{ + fs_modify_word(fs, name, FS_BINARY, free_, len, value); +} + uint64_t fs_get_uint64_by_index(fieldset_t *fs, int index) { return (uint64_t) fs->fields[index].value; diff --git a/src/fieldset.h b/src/fieldset.h index 9d49ba3..66de892 100644 --- a/src/fieldset.h +++ b/src/fieldset.h @@ -74,6 +74,8 @@ int fds_get_index_by_name(fielddefset_t *fds, char *name); void gen_fielddef_set(fielddefset_t *fds, fielddef_t fs[], int len); +void fs_add_null(fieldset_t *fs, const char *name); + void fs_add_uint64(fieldset_t *fs, const char *name, uint64_t value); void fs_add_string(fieldset_t *fs, const char *name, char *value, int free_); @@ -81,9 +83,17 @@ void fs_add_string(fieldset_t *fs, const char *name, char *value, int free_); void fs_add_binary(fieldset_t *fs, const char *name, size_t len, void *value, int free_); -uint64_t fs_get_uint64_by_index(fieldset_t *fs, int index); +// Modify +void fs_modify_null(fieldset_t *fs, const char *name); -void fs_add_null(fieldset_t *fs, const char *name); +void fs_modify_uint64(fieldset_t *fs, const char *name, uint64_t value); + +void fs_modify_string(fieldset_t *fs, const char *name, char *value, int free_); + +void fs_modify_binary(fieldset_t *fs, const char *name, size_t len, + void *value, int free_); + +uint64_t fs_get_uint64_by_index(fieldset_t *fs, int index); void fs_free(fieldset_t *fs); diff --git a/src/probe_modules/module_udp.c b/src/probe_modules/module_udp.c index 3f78819..8bb8b99 100644 --- a/src/probe_modules/module_udp.c +++ b/src/probe_modules/module_udp.c @@ -36,6 +36,24 @@ int udp_send_msg_len = 0; const char *udp_send_msg_default = "GET / HTTP/1.1\r\nHost: www\r\n\r\n"; +const char *udp_unreach_strings[] = { +"network unreachable", +"host unreachable", +"protocol unreachable", +"port unreachable", +"fragments required", +"source route failed", +"network unknown", +"host unknown", +"source host isolated", +"network admin. prohibited", +"host admin. prohibited", +"network unreachable TOS", +"host unreachable TOS", +"communication admin. prohibited", +"host presdence violation", +"precedence cutoff"}; + static int num_ports; probe_module_t module_udp; @@ -202,25 +220,39 @@ void udp_process_packet(const u_char *packet, UNUSED uint32_t len, fieldset_t *f fs_add_uint64(fs, "success", 1); fs_add_uint64(fs, "sport", ntohs(udp->source)); fs_add_uint64(fs, "dport", ntohs(udp->dest)); + fs_add_null(fs, "icmp_responder"); fs_add_null(fs, "icmp_type"); fs_add_null(fs, "icmp_code"); + fs_add_null(fs, "icmp_unreach_str"); fs_add_binary(fs, "data", (ntohs(udp->len) - sizeof(struct udphdr)), (void*) &udp[1], 0); } else if (ip_hdr->protocol == IPPROTO_ICMP) { struct icmphdr *icmp = (struct icmphdr *)((char *)ip_hdr + ip_hdr->ihl * 4); + struct iphdr *ip_inner = (struct iphdr*)&icmp[1]; + // ICMP unreach comes from another server (not the one we sent a probe to); + // But we will fix up saddr to be who we sent the probe to, in case you care. + fs_modify_string(fs, "saddr", make_ip_str(ip_inner->daddr), 1); fs_add_string(fs, "classification", (char*) "icmp-unreach", 0); fs_add_uint64(fs, "success", 0); fs_add_null(fs, "sport"); fs_add_null(fs, "dport"); + fs_add_string(fs, "icmp_responder", make_ip_str(ip_hdr->saddr), 1); fs_add_uint64(fs, "icmp_type", icmp->type); fs_add_uint64(fs, "icmp_code", icmp->code); + if (icmp->code <= ICMP_PREC_CUTOFF) { + fs_add_string(fs, "icmp_unreach_str", (char *)udp_unreach_strings[icmp->code], 0); + } else { + fs_add_string(fs, "icmp_unreach_str", (char *)"unknown", 0); + } fs_add_null(fs, "data"); } else { fs_add_string(fs, "classification", (char*) "other", 0); fs_add_uint64(fs, "success", 0); fs_add_null(fs, "sport"); fs_add_null(fs, "dport"); + fs_add_null(fs, "icmp_responder"); fs_add_null(fs, "icmp_type"); fs_add_null(fs, "icmp_code"); + fs_add_null(fs, "icmp_unreach_str"); fs_add_null(fs, "data"); } } @@ -252,7 +284,7 @@ int udp_validate_packet(const struct iphdr *ip_hdr, uint32_t len, if (icmp->type != ICMP_DEST_UNREACH) { return 0; } - + struct iphdr *ip_inner = (struct iphdr*)&icmp[1]; // Now we know the actual inner ip length, we should recheck the buffer if (len < 4*ip_inner->ihl - sizeof(struct iphdr) + min_len) { @@ -262,7 +294,7 @@ int udp_validate_packet(const struct iphdr *ip_hdr, uint32_t len, struct udphdr *udp = (struct udphdr *)((char*)ip_inner + 4*ip_inner->ihl); sport = ntohs(udp->source); - dport = ntohs(udp->dest); + dport = ntohs(udp->dest); } else { return 0; } @@ -280,8 +312,10 @@ static fielddef_t fields[] = { {.name = "success", .type="int", .desc = "is response considered success"}, {.name = "sport", .type = "int", .desc = "UDP source port"}, {.name = "dport", .type = "int", .desc = "UDP destination port"}, + {.name = "icmp_responder", .type = "string", .desc = "Source IP of ICMP_UNREACH message"}, {.name = "icmp_type", .type = "int", .desc = "icmp message type"}, {.name = "icmp_code", .type = "int", .desc = "icmp message sub type code"}, + {.name = "icmp_unreach_str", .type = "string", .desc = "for icmp_unreach responses, the string version of icmp_code (e.g. network-unreach)"}, {.name = "data", .type="binary", .desc = "UDP payload"} }; @@ -299,6 +333,6 @@ probe_module_t module_udp = { .process_packet = &udp_process_packet, .close = &udp_global_cleanup, .fields = fields, - .numfields = sizeof(fields)/sizeof(fields[0]) + .numfields = sizeof(fields)/sizeof(fields[0]) }; diff --git a/src/probe_modules/packet.c b/src/probe_modules/packet.c index 0ff752a..47d251f 100644 --- a/src/probe_modules/packet.c +++ b/src/probe_modules/packet.c @@ -20,6 +20,7 @@ #include #include +#include #include "state.h" @@ -121,3 +122,16 @@ void make_udp_header(struct udphdr *udp_header, port_h_t dest_port, udp_header->check = 0; } +// Note: caller must free return value +char *make_ip_str(uint32_t ip) +{ + struct in_addr t; + t.s_addr = ip; + const char *temp = inet_ntoa(t); + char *retv = malloc(strlen(temp)+1); + assert (retv); + strcpy(retv, temp); + return retv; +} + + diff --git a/src/probe_modules/packet.h b/src/probe_modules/packet.h index 1db5d6d..77f3827 100644 --- a/src/probe_modules/packet.h +++ b/src/probe_modules/packet.h @@ -102,4 +102,7 @@ static __attribute__((unused)) inline uint16_t get_src_port(int num_ports, return zconf.source_port_first + ((validation[1] + probe_num) % num_ports); } +// Note: caller must free return value +char *make_ip_str(uint32_t ip); + #endif diff --git a/src/probe_modules/probe_modules.c b/src/probe_modules/probe_modules.c index fdab124..f28d9bf 100644 --- a/src/probe_modules/probe_modules.c +++ b/src/probe_modules/probe_modules.c @@ -21,6 +21,7 @@ #include "../../lib/logger.h" #include "../fieldset.h" #include "probe_modules.h" +#include "packet.h" extern probe_module_t module_tcp_synscan; extern probe_module_t module_icmp_echo; @@ -53,17 +54,6 @@ void print_probe_modules(void) } } -char *make_ip_str(uint32_t ip) -{ - struct in_addr t; - t.s_addr = ip; - const char *temp = inet_ntoa(t); - char *retv = malloc(strlen(temp)+1); - assert (retv); - strcpy(retv, temp); - return retv; -} - void fs_add_ip_fields(fieldset_t *fs, struct iphdr *ip) { fs_add_string(fs, "saddr", make_ip_str(ip->saddr), 1); diff --git a/src/zmap.1 b/src/zmap.1 index 0ab49af..3582c07 100644 --- a/src/zmap.1 +++ b/src/zmap.1 @@ -24,7 +24,7 @@ File of subnets to exclude, in CIDR notation (e.g. 192.168.0.0/16), one-per line. It is recommended you use this to exclude RFC 1918 addresses, multicast, IANA reserved space, and other IANA special-purpose addresses. An example blacklist file is provided in -.B conf/blacklist.example +.B conf/blacklist.conf for this purpose. .TP .B -w, --whitelist-file=path @@ -98,6 +98,11 @@ Select probe module (default=tcp_synscan) .TP .B \-O, --output-module=name Select output module (default=simple_file) +.TP +.B \-f, --output-fields=fields +Fields that should be output in result set; see +.B --list-output-fields + .TP .B --probe-args=args Arguments to pass to probe module @@ -110,12 +115,18 @@ List available output modules .TP .B --list-probe-modules List available probe modules +.TP +.B --list-output-fields +List all fields that can be output (using +.B --output-fields +) +by selected probe module .SS "Additional options" .TP .B \-C, --config=filename Read a configuration file, which can specify -any of these options (default=zmap.conf) +any of these options (default=/etc/zmap/zmap.conf) .TP .B \-q, --quiet Do not print status updates