Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

the --max-targets flag with a percent wasn't respecting multi-ports #886

Merged
merged 16 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions lib/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,24 +144,28 @@ void fprintw(FILE *f, const char *s, size_t w)
free(news);
}

uint32_t parse_max_hosts(char *max_targets)
// parse a string representing a number/percentage of targets
// A percentage is interpreted as percent of the search space, so 2^32 * port_count
uint64_t parse_max_targets(char *max_targets, int port_count)
{
assert(port_count > 0);
char *end;
errno = 0;
double v = strtod(max_targets, &end);
if (end == max_targets || errno != 0) {
log_fatal("argparse", "can't convert max-targets to a number");
}
if (end[0] == '%' && end[1] == '\0') {
// treat as percentage
v = v * ((unsigned long long int)1 << 32) / 100.;
// treat as percentage of the search space, so percent of 2^32 * num_ports
uint64_t search_space = ((uint64_t)1 << 32) * port_count;
v = v * search_space / 100.;
} else if (end[0] != '\0') {
log_fatal("eargparse", "extra characters after max-targets");
}
if (v <= 0) {
return 0;
} else if (v >= ((unsigned long long int)1 << 32)) {
return 0xFFFFFFFF;
} else if (v >= (((uint64_t)1 << 32) * port_count)) {
return (((uint64_t)1 << 32) * port_count);
} else {
return v;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ int max_int(int a, int b);
int min_int(int a, int b);
uint64_t min_uint64_t(uint64_t a, uint64_t b);

uint32_t parse_max_hosts(char *max_targets);
uint64_t parse_max_targets(char *max_targets, int port_count);
void enforce_range(const char *name, int v, int min, int max);

// Splits comma delimited string into char*[]. Does not handle
Expand Down
4 changes: 2 additions & 2 deletions src/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ struct state_send {
int warmup;
int complete;
uint32_t first_scanned;
uint32_t max_targets;
uint64_t max_targets;
uint32_t sendto_failures;
uint32_t max_index;
uint16_t max_port_index;
Expand Down Expand Up @@ -215,7 +215,7 @@ struct state_recv {
extern struct state_recv zrecv;

struct port_conf {
int port_count;
uint port_count;
uint16_t ports[0xFFFF + 1];
uint8_t *port_bitmap;
};
Expand Down
2 changes: 1 addition & 1 deletion src/summary.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ void json_metadata(FILE *file)
json_object_new_int(zconf.source_port_last));

json_object *target_ports = json_object_new_array();
for (int i = 0; i < zconf.ports->port_count; i++) {
for (uint i = 0; i < zconf.ports->port_count; i++) {
json_object_array_add(
target_ports,
json_object_new_int(zconf.ports->ports[i]));
Expand Down
2 changes: 1 addition & 1 deletion src/ziterate.1
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" https://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "ZITERATE" "1" "February 2024" "ZMap" "ziterate"
.TH "ZITERATE" "1" "May 2024" "ZMap" "ziterate"
.
.SH "NAME"
\fBziterate\fR \- ZMap IP permutation generation file
Expand Down
2 changes: 1 addition & 1 deletion src/ziterate.1.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/ziterate.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ int main(int argc, char **argv)
conf.destination_cidrs_len = args.inputs_num;
// max targets
if (args.max_targets_given) {
conf.max_hosts = parse_max_hosts(args.max_targets_arg);
conf.max_hosts = parse_max_targets(args.max_targets_arg, zconf.ports->port_count);
}

// sanity check blocklist file
Expand Down
4 changes: 2 additions & 2 deletions src/zmap.1
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" https://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "ZMAP" "1" "April 2024" "ZMap" "zmap"
.TH "ZMAP" "1" "July 2024" "ZMap" "zmap"
.
.SH "NAME"
\fBzmap\fR \- The Fast Internet Scanner
Expand Down Expand Up @@ -52,7 +52,7 @@ Set the send rate in bits/second (supports suffixes G, M, and K (e\.g\. \-B 10M
.
.TP
\fB\-n\fR, \fB\-\-max\-targets=n\fR
Cap the number of targets to probe\. This can either be a number (e\.g\. \-n 1000) or a percentage (e\.g\. \-n 0\.1%) of the scannable address space (after excluding blocklist)\. A target is an IP/port pair, if scanning multiple ports, and an IP otherwise\.
Cap the number of targets to probe\. This can either be a number (e\.g\. \-n 1000) or a percentage (e\.g\. \-n 0\.1%) of the scannable address space (after excluding blocklist)\. A target is an IP/port pair, if scanning multiple ports, and an IP otherwise\. In the case of percents and multiple ports, the percent is of the total number of IP/port pair combinations\.
.
.TP
\fB\-N\fR, \fB\-\-max\-results=n\fR
Expand Down
5 changes: 3 additions & 2 deletions src/zmap.1.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion src/zmap.1.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ on a gigabit network connection, reaching ~98% theoretical line speed.
Cap the number of targets to probe. This can either be a number (e.g. -n
1000) or a percentage (e.g. -n 0.1%) of the scannable address space
(after excluding blocklist). A target is an IP/port pair, if scanning multiple
ports, and an IP otherwise.
ports, and an IP otherwise. In the case of percents and multiple ports, the percent
is of the total number of IP/port pair combinations.

* `-N`, `--max-results=n`:
Exit after receiving this many results
Expand Down
2 changes: 1 addition & 1 deletion src/zmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -928,7 +928,7 @@ int main(int argc, char *argv[])
}

if (args.max_targets_given) {
zconf.max_targets = parse_max_hosts(args.max_targets_arg);
zconf.max_targets = parse_max_targets(args.max_targets_arg, zconf.ports->port_count);
}

// blocklist
Expand Down
2 changes: 1 addition & 1 deletion src/zopt.ggo.in
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ option "bandwidth" B "Set send rate in bits/second (supports suffix
option "batch" - "Set batch size for how many packets to send in a single syscall. Advantageous on Linux or with netmap (default=64)"
typestr="pps"
optional int
option "max-targets" n "Cap number of targets to probe (as a number or a percentage of the address space). A target is an IP/port pair, if scanning multiple ports, and an IP otherwise."
option "max-targets" n "Cap number of targets to probe (as a number '-n 1000' or a percentage '-n 1%' of the target search space). A target is an IP/port pair, if scanning multiple ports, and an IP otherwise."
typestr="n"
optional string
option "max-runtime" t "Cap length of time for sending packets"
Expand Down
22 changes: 21 additions & 1 deletion test/integration-tests/test_dryrun_tcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ def test_multi_port_with_subnet_and_threads():
for test_iter in range(5): # using iterations to check for bugs in shard code
for port_test_index in range(len(port_tests)):
expected_port_set = set(expected_ports[port_test_index])
packet_list = zmap_wrapper.Wrapper(port=port_tests[port_test_index], subnet=subnet, threads=thread_ct).run()
packet_list = zmap_wrapper.Wrapper(port=port_tests[port_test_index], subnet=subnet,
threads=thread_ct).run()
assert len(packet_list) == len(expected_ips) * len(expected_ports[port_test_index]), ("incorrect "
"number of "
"packets sent")
Expand Down Expand Up @@ -572,3 +573,22 @@ def test_ip_layer_option():
ip_list = [packet["ip"]["daddr"] for packet in packets]
for actual_ip in ip_list:
assert actual_ip in expected_scanned_ips, "an IP was not scanned"


## --max-targets
def test_max_targets_option():
"""
scan using various numbers of max targets and ensure the correct number of packets are sent
"""
tests = [
# Format: (max_targets, ports, expected_num_ips)
("5", "80", 5),
("109", "80", 109),
("10", "80-81", 10), # target is IP + port, so specifying multiple ports should not affect the number of IPs
("0.0001%", "80", 4294), # 0.0001% of the IPv4 space, rounded down
("0.0001%", "80-81", 8589) # 0.0001% of the IPv4 space over 2 ports, rounded down
]
for max_targets, ports, expected_num_ips in tests:
packets = zmap_wrapper.Wrapper(threads=1, max_targets=max_targets, port=ports).run()
assert len(
packets) == expected_num_ips, "incorrect number of packets sent for test with max_targets = " + max_targets + " and ports = " + ports
5 changes: 4 additions & 1 deletion test/integration-tests/zmap_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
class Wrapper:
def __init__(self, port="80", subnet="", num_of_ips=-1, threads=-1, shards=-1, shard=-1, seed=-1, iplayer=False,
dryrun=True, output_file="", max_runtime=-1, max_cooldown=-1, blocklist_file="", allowlist_file="",
list_of_ips_file="", probes="", source_ip="", source_port="", source_mac="", rate=-1):
list_of_ips_file="", probes="", source_ip="", source_port="", source_mac="", rate=-1, max_targets=""):
self.port = port
self.subnet = subnet
self.num_of_ips = num_of_ips
Expand All @@ -29,6 +29,7 @@ def __init__(self, port="80", subnet="", num_of_ips=-1, threads=-1, shards=-1, s
self.source_port = source_port
self.source_mac = source_mac
self.rate = rate
self.max_targets = max_targets


def run(self):
Expand Down Expand Up @@ -72,6 +73,8 @@ def run(self):
args.extend(["--source-mac=" + self.source_mac])
if self.rate != -1:
args.extend(["--rate=" + str(self.rate)])
if self.max_targets != "":
args.extend(["--max-targets=" + str(self.max_targets)])

test_output = subprocess.run(args, stdout=subprocess.PIPE).stdout.decode('utf-8')
packets = parse_output_into_obj_list(test_output)
Expand Down
Loading