-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathdns.rs
146 lines (120 loc) · 4.79 KB
/
dns.rs
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! Use the DNS provider to test a few things regarding user traces
use std::process::Command;
use std::time::Duration;
use ferrisetw::parser::Parser;
use ferrisetw::provider::{EventFilter, Provider};
use ferrisetw::schema::Schema;
use ferrisetw::schema_locator::SchemaLocator;
use ferrisetw::trace::TraceTrait;
use ferrisetw::trace::UserTrace;
use ferrisetw::EventRecord;
mod utils;
use utils::{Status, TestKind};
const TEST_DOMAIN_NAME: &str = "www.github.com";
const EVENT_ID_DNS_QUERY_INITIATED: u16 = 3006;
const EVENT_ID_DNS_QUERY_COMPLETED: u16 = 3008;
#[test]
fn dns_tests() {
// These tests must be consecutive, as they share the same DNS provider
simple_user_dns_trace();
test_event_id_filter();
}
fn simple_user_dns_trace() {
let passed = Status::new(TestKind::ExpectSuccess);
let notifier = passed.notifier();
let dns_provider = Provider::by_guid("1c95126e-7eea-49a9-a3fe-a378b03ddb4d") // Microsoft-Windows-DNS-Client
.add_callback(
move |record: &EventRecord, schema_locator: &SchemaLocator| {
let schema = schema_locator.event_schema(record).unwrap();
let parser = Parser::create(record, &schema);
// While we're at it, let's check a few more-or-less unrelated things on an actual ETW event
check_a_few_cases(record, &parser, &schema);
if has_seen_resolution_to_test_domain(record, &parser) {
notifier.notify_success();
}
},
)
.build();
let dns_trace = UserTrace::new()
.enable(dns_provider)
.start_and_process()
.unwrap();
generate_dns_events();
passed.assert_passed();
assert!(dns_trace.events_handled() > 0);
dns_trace.stop().unwrap();
println!("simple_user_dns_trace passed");
}
fn test_event_id_filter() {
let passed1 = Status::new(TestKind::ExpectSuccess);
let passed2 = Status::new(TestKind::ExpectNoFailure);
let passed3 = Status::new(TestKind::ExpectSuccess);
let notifier1 = passed1.notifier();
let notifier2 = passed2.notifier();
let notifier3 = passed3.notifier();
let filter = EventFilter::ByEventIds(vec![EVENT_ID_DNS_QUERY_COMPLETED]);
let dns_provider = Provider::by_guid("1c95126e-7eea-49a9-a3fe-a378b03ddb4d") // Microsoft-Windows-DNS-Client
.add_filter(filter)
.add_callback(
move |record: &EventRecord, _schema_locator: &SchemaLocator| {
// We want at least one event, but only for the filtered kind
if record.event_id() == EVENT_ID_DNS_QUERY_COMPLETED {
notifier1.notify_success();
} else {
notifier2.notify_failure();
}
},
)
.add_callback(
move |record: &EventRecord, _schema_locator: &SchemaLocator| {
// This secondary callback basically tests all callbacks are run
if record.event_id() == EVENT_ID_DNS_QUERY_COMPLETED {
notifier3.notify_success();
}
},
)
.build();
let _trace = UserTrace::new()
.enable(dns_provider)
.start_and_process()
.unwrap();
generate_dns_events();
passed1.assert_passed();
passed2.assert_passed();
passed3.assert_passed();
// Not calling .stop() here, let's just rely on the `impl Drop`
println!("test_event_id_filter passed");
}
fn generate_dns_events() {
std::thread::sleep(Duration::from_secs(1));
// Unfortunately, `&str::to_socket_addrs()` does not use Microsoft APIs, and hence does not trigger a DNS ETW event
// Let's use ping.exe instead
println!("Resolving {}...", TEST_DOMAIN_NAME);
let _output = Command::new("ping.exe")
.arg("-n")
.arg("1")
.arg(TEST_DOMAIN_NAME)
.output()
.unwrap();
println!("Resolution done.");
}
fn check_a_few_cases(record: &EventRecord, parser: &Parser, schema: &Schema) {
// Parsing with a wrong type should properly error out
if record.event_id() == EVENT_ID_DNS_QUERY_INITIATED {
let _right_type: String = parser.try_parse("QueryName").unwrap();
let wrong_type = parser.try_parse::<u32>("QueryName");
assert!(wrong_type.is_err());
}
// Giving an unknown property should properly error out
let wrong_name = parser.try_parse::<u32>("NoSuchProperty");
assert!(wrong_name.is_err());
assert_eq!(&schema.provider_name(), "Microsoft-Windows-DNS-Client");
}
fn has_seen_resolution_to_test_domain(record: &EventRecord, parser: &Parser) -> bool {
if record.event_id() == EVENT_ID_DNS_QUERY_INITIATED {
let query_name: String = parser.try_parse("QueryName").unwrap();
#[allow(unused_parens)]
return (query_name == TEST_DOMAIN_NAME);
}
false
}