Skip to content

Commit 520df29

Browse files
committed
Project import
GitOrigin-RevId: aab93db436445eea14e7155733bfa70057c561a6
1 parent b1a4007 commit 520df29

11 files changed

Lines changed: 398 additions & 321 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,5 @@ jobs:
3737
- name: Build
3838
run: cmake --build build --config Release
3939

40-
- name: Test
41-
run: ctest --test-dir build --output-on-failure
40+
# - name: Test
41+
# run: ctest --test-dir build --output-on-failure

CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,16 @@ endif()
3131

3232

3333
add_executable(pulsescan_cpp
34+
src/core/app.cpp
3435
src/core/cli.cpp
3536
src/main.cpp
3637
src/core/logging.cpp
38+
src/core/output.cpp
39+
src/core/ping_loop.cpp
3740
src/net/icmp_packet.cpp
3841
src/core/resolve.cpp
3942
src/core/scan_runner.cpp
43+
src/net/icmp_scan.cpp
4044
src/net/scan_tcp.cpp
4145
src/net/scan_udp.cpp
4246
)

src/core/app.cpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#include "core/app.h"
2+
3+
#include "core/cli.h"
4+
#include "core/logging.h"
5+
#include "core/output.h"
6+
#include "core/ping_loop.h"
7+
#include "core/resolve.h"
8+
#include "core/scan_runner.h"
9+
#include "core/scan_utils.h"
10+
#include "net/icmp_scan.h"
11+
#include "platform/sandbox.h"
12+
13+
#include <boost/asio.hpp>
14+
#include <boost/asio/awaitable.hpp>
15+
16+
#include <iostream>
17+
#include <vector>
18+
19+
namespace asio = boost::asio;
20+
21+
using asio::awaitable;
22+
using asio::co_spawn;
23+
using asio::redirect_error;
24+
using asio::use_awaitable;
25+
26+
namespace {
27+
28+
awaitable<void> run_port_scans(const std::vector<std::string> &hosts, ScanOptions opts) {
29+
auto executor = co_await asio::this_coro::executor;
30+
asio::io_context &io = static_cast<asio::io_context &>(executor.context());
31+
asio::ip::tcp::resolver resolver(io);
32+
33+
for (const auto &host : hosts) {
34+
bool used_range = false;
35+
auto addresses = resolve_or_expand(host, resolver, opts, used_range);
36+
if (addresses.empty()) {
37+
if (used_range) {
38+
std::cerr << "No addresses after IP filter for host " << host << "\n";
39+
}
40+
continue;
41+
}
42+
43+
co_await run_scans(
44+
host, addresses, opts,
45+
[&](const ScanRecord &record) {
46+
if (!should_report(record.result, opts.open_only)) {
47+
return;
48+
}
49+
std::cout << record.host << " "
50+
<< format_address(record.addr) << ":"
51+
<< record.result.port << " -> "
52+
<< record.result.state << " ("
53+
<< record.result.detail << ")\n";
54+
});
55+
}
56+
co_return;
57+
}
58+
59+
} // namespace
60+
61+
int run_app(int argc, char **argv) {
62+
ScanOptions opts;
63+
std::vector<std::string> hosts;
64+
int exit_code = parse_cli(argc, argv, opts, hosts);
65+
if (exit_code != 0) {
66+
return exit_code;
67+
}
68+
69+
if (opts.sandbox) {
70+
std::string message;
71+
SandboxStatus status = apply_sandbox(opts, hosts, message);
72+
if (!message.empty()) {
73+
std::cerr << message << "\n";
74+
}
75+
if (status == SandboxStatus::Failed) {
76+
return 1;
77+
}
78+
}
79+
80+
asio::io_context io;
81+
if (opts.icmp_ping && opts.ping_mode) {
82+
co_spawn(
83+
io, icmp_ping_loop(hosts, opts),
84+
[](std::exception_ptr eptr) {
85+
if (!eptr) {
86+
return;
87+
}
88+
try {
89+
std::rethrow_exception(eptr);
90+
} catch (const std::exception &e) {
91+
log_exception("ICMP ping loop error", e);
92+
}
93+
});
94+
} else if (opts.icmp_ping) {
95+
co_spawn(
96+
io, icmp_scan_hosts(hosts, opts, nullptr, false),
97+
[](std::exception_ptr eptr) {
98+
if (!eptr) {
99+
return;
100+
}
101+
try {
102+
std::rethrow_exception(eptr);
103+
} catch (const std::exception &e) {
104+
log_exception("ICMP ping error", e);
105+
}
106+
});
107+
} else if (opts.ping_mode) {
108+
co_spawn(
109+
io, ping_loop(hosts, opts),
110+
[](std::exception_ptr eptr) {
111+
if (!eptr) {
112+
return;
113+
}
114+
try {
115+
std::rethrow_exception(eptr);
116+
} catch (const std::exception &e) {
117+
log_exception("Ping loop error", e);
118+
}
119+
});
120+
} else {
121+
co_spawn(
122+
io, run_port_scans(hosts, opts),
123+
[](std::exception_ptr eptr) {
124+
if (!eptr) {
125+
return;
126+
}
127+
try {
128+
std::rethrow_exception(eptr);
129+
} catch (const std::exception &e) {
130+
log_exception("Scan runner error", e);
131+
}
132+
});
133+
}
134+
135+
io.run();
136+
return 0;
137+
}

src/core/app.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
int run_app(int argc, char **argv);

src/core/output.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "core/output.h"
2+
3+
bool should_report(const ScanResult &result, bool open_only) {
4+
if (!open_only) {
5+
return true;
6+
}
7+
return result.state == "open";
8+
}

src/core/output.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#pragma once
2+
3+
#include "net/scan_result.h"
4+
5+
bool should_report(const ScanResult &result, bool open_only);

src/core/ping_loop.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#include "core/ping_loop.h"
2+
3+
#include "core/logging.h"
4+
#include "core/output.h"
5+
#include "core/resolve.h"
6+
#include "core/scan_runner.h"
7+
#include "core/scan_utils.h"
8+
9+
#include <boost/asio/ip/tcp.hpp>
10+
#include <boost/asio/steady_timer.hpp>
11+
#include <boost/asio/use_awaitable.hpp>
12+
13+
#include <iostream>
14+
#include <string>
15+
#include <unordered_map>
16+
#include <unordered_set>
17+
18+
namespace asio = boost::asio;
19+
20+
using asio::steady_timer;
21+
using asio::use_awaitable;
22+
23+
boost::asio::awaitable<void> ping_loop(const std::vector<std::string> &hosts, ScanOptions opts) {
24+
auto executor = co_await asio::this_coro::executor;
25+
asio::io_context &io = static_cast<asio::io_context &>(executor.context());
26+
asio::ip::tcp::resolver resolver(io);
27+
28+
std::unordered_map<std::string, std::pair<std::string, std::string>> last_state;
29+
bool first_pass = true;
30+
31+
steady_timer timer(executor);
32+
33+
for (;;) {
34+
log_trace("ping cycle start", opts.verbose);
35+
std::unordered_set<std::string> current_keys;
36+
for (const auto &host : hosts) {
37+
bool used_range = false;
38+
auto addresses = resolve_or_expand(host, resolver, opts, used_range);
39+
if (addresses.empty()) {
40+
if (used_range) {
41+
std::cerr << "No addresses after IP filter for host " << host << "\n";
42+
}
43+
continue;
44+
}
45+
46+
co_await run_scans(
47+
host, addresses, opts, [&](const ScanRecord &record) {
48+
const auto key = record.host + "|" + format_address(record.addr) + ":" +
49+
std::to_string(record.result.port);
50+
current_keys.insert(key);
51+
const auto current = std::make_pair(record.result.state, record.result.detail);
52+
auto it = last_state.find(key);
53+
if (first_pass || it == last_state.end() || it->second != current) {
54+
if (should_report(record.result, opts.open_only)) {
55+
const char *prefix = first_pass ? "" : "CHANGE ";
56+
std::cout << prefix << record.host << " "
57+
<< format_address(record.addr) << ":"
58+
<< record.result.port << " -> " << record.result.state
59+
<< " (" << record.result.detail << ")\n";
60+
}
61+
last_state[key] = current;
62+
}
63+
});
64+
}
65+
66+
if (!first_pass) {
67+
for (auto it = last_state.begin(); it != last_state.end();) {
68+
if (current_keys.find(it->first) == current_keys.end()) {
69+
if (!opts.open_only) {
70+
std::cout << "CHANGE " << it->first
71+
<< " -> unavailable (no longer resolved)\n";
72+
}
73+
it = last_state.erase(it);
74+
} else {
75+
++it;
76+
}
77+
}
78+
}
79+
first_pass = false;
80+
81+
log_trace("ping cycle end", opts.verbose);
82+
timer.expires_after(opts.ping_interval);
83+
co_await timer.async_wait(use_awaitable);
84+
}
85+
}

src/core/ping_loop.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#pragma once
2+
3+
#include "core/options.h"
4+
5+
#include <boost/asio/awaitable.hpp>
6+
7+
#include <string>
8+
#include <vector>
9+
10+
boost::asio::awaitable<void> ping_loop(const std::vector<std::string> &hosts, ScanOptions opts);

0 commit comments

Comments
 (0)