-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwalk.cpp
More file actions
109 lines (98 loc) · 4.52 KB
/
Copy pathwalk.cpp
File metadata and controls
109 lines (98 loc) · 4.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// SNMPv3 authPriv Walk, streamed, in the coroutine form: SHA-1, and AES-256 under the Reeder Key
// Extension. Each batch is printed as it arrives and nothing is buffered -- the core form of a
// Walk (ADR-0004), where walk-collect.cpp shows the convenience over it.
//
// ./example-walk 127.0.0.1 16161 privsha1aes256c snmpio-interop 1.3.6.1.2.1.1 [limit]
//
// With a limit, the Walk is cancelled once that many rows have been printed, and ends in
// Errc::WalkIncomplete -- a Walk stopped early never reports itself as a whole one.
#include <charconv>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <optional>
#include <span>
#include <string>
#include <string_view>
#include <system_error>
#include <snmpio/Client.hpp>
namespace net = snmpio::net;
net::Awaitable<void> run(snmpio::Client& client, snmpio::Target target,
snmpio::Credentials credentials, snmpio::Oid base,
std::optional<std::size_t> limit) {
// GETBULK, ten rows a round trip, is the default; it is spelled out here only to show the knob.
// Zero walks with GETNEXT instead, one row a round trip: slower, and what an Agent that
// mishandles GETBULK needs.
snmpio::WalkOptions options;
options.maxRepetitions = 10;
// A `total` cancellation lets the batch in hand finish and then stops the Walk cleanly, where a
// `terminal` one would drop it at once. Returning false from the handler is the other way to
// stop; the signal is the one that also reaches a Walk from outside it, a deadline say.
net::asio::cancellation_signal cancelWalk;
std::size_t rows = 0;
bool withheld = false; // rows of a batch the limit kept from being printed
auto onBatch = [&rows, &withheld, &cancelWalk, limit](std::span<const snmpio::Varbind> batch) {
for (const auto& vb : batch) {
if (limit && rows == *limit) {
withheld = true;
break;
}
std::cout << vb.name.toString() << " = " << snmpio::toString(vb.val) << "\n";
++rows;
}
// The handler runs on the Client's strand, which is where the Walk observes its cancellation
// state, so emitting from here needs no hop.
if (limit && rows == *limit) cancelWalk.emit(net::asio::cancellation_type::total);
return true;
};
net::ErrorCode ec;
co_await client.asyncWalk(
std::move(target), std::move(credentials), std::move(base), options, onBatch,
net::asio::bind_cancellation_slot(cancelWalk.slot(),
net::asio::redirect_error(net::asio::use_awaitable, ec)));
// The Walk looks at the signal before its next request, so when the batch that crossed the
// limit was also its last, it completes as a whole one -- as it was, but not as printed. What
// the handler withheld makes this an incomplete Walk all the same.
if (!ec && withheld) ec = snmpio::make_error_code(snmpio::Errc::WalkIncomplete);
if (ec) std::cerr << "walk ended after " << rows << " rows: " << ec.message() << "\n";
client.stop();
}
int main(int argc, char** argv) {
if (argc != 6 && argc != 7) {
std::cerr << "usage: " << argv[0] << " <address> <port> <user> <password> <base-oid> [limit]\n";
return 2;
}
snmpio::Target target;
target.endpoint = {net::asio::ip::make_address(argv[1]),
static_cast<std::uint16_t>(std::atoi(argv[2]))};
// AES-256 needs more key material than SHA-1 produces, and the two Key Extensions make it in
// mutually incompatible ways. Aes256C is Reeder's, Aes256 Blumenthal's: the Agent's user
// decides which, and nothing on the wire says. Usm.hpp lists the rest.
snmpio::Credentials credentials;
credentials.userName = argv[3];
credentials.level = snmpio::SecurityLevel::AuthPriv;
credentials.authProtocol = snmpio::AuthProtocol::Sha1;
credentials.authPassword = argv[4];
credentials.privProtocol = snmpio::PrivProtocol::Aes256C;
credentials.privPassword = argv[4];
const auto base = snmpio::Oid::parse(argv[5]);
if (!base) {
std::cerr << "bad base OID: " << argv[5] << "\n";
return 2;
}
std::optional<std::size_t> limit;
if (argc == 7) {
const std::string_view arg = argv[6];
std::size_t n = 0;
const auto [end, err] = std::from_chars(arg.data(), arg.data() + arg.size(), n);
if (err != std::errc{} || end != arg.data() + arg.size() || n == 0) {
std::cerr << "bad limit: " << arg << "\n";
return 2;
}
limit = n;
}
net::IoContext io;
snmpio::Client client(io.get_executor());
net::asio::co_spawn(io, run(client, target, credentials, *base, limit), net::asio::detached);
io.run();
}