1 // Copyright 2022, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This module provides convenience functions for doh logging.
16 
17 use crate::connection::driver::Cause;
18 use crate::connection::driver::HandshakeInfo;
19 use crate::connection::driver::HandshakeResult;
20 use statslog_rust::network_dns_handshake_reported::{
21     Cause as StatsdCause, NetworkDnsHandshakeReported, NetworkType as StatsdNetworkType,
22     PrivateDnsMode as StatsdPrivateDnsMode, Protocol as StatsdProtocol, Result as StatsdResult,
23 };
24 
25 const CELLULAR: u32 = 1;
26 const WIFI: u32 = 2;
27 const BLUETOOTH: u32 = 3;
28 const ETHERNET: u32 = 4;
29 const VPN: u32 = 5;
30 const WIFI_AWARE: u32 = 6;
31 const LOWPAN: u32 = 7;
32 const CELLULAR_VPN: u32 = 8;
33 const WIFI_VPN: u32 = 9;
34 const BLUETOOTH_VPN: u32 = 10;
35 const ETHERNET_VPN: u32 = 11;
36 const WIFI_CELLULAR_VPN: u32 = 12;
37 
38 const OFF: u32 = 1;
39 const OPPORTUNISTIC: u32 = 2;
40 const STRICT: u32 = 3;
41 
42 const TLS1_3_VERSION: u32 = 3;
43 
create_default_handshake_atom() -> NetworkDnsHandshakeReported44 fn create_default_handshake_atom() -> NetworkDnsHandshakeReported {
45     NetworkDnsHandshakeReported {
46         protocol: StatsdProtocol::ProtoUnknown,
47         result: StatsdResult::HrUnknown,
48         cause: StatsdCause::HcUnknown,
49         network_type: StatsdNetworkType::NtUnknown,
50         private_dns_mode: StatsdPrivateDnsMode::PdmUnknown,
51         latency_micros: -1,
52         bytes_sent: -1,
53         bytes_received: -1,
54         round_trips: -1,
55         tls_session_cache_hit: false,
56         tls_version: -1,
57         hostname_verification: false,
58         quic_version: -1,
59         server_index: -1,
60         sampling_rate_denom: -1,
61     }
62 }
63 
construct_handshake_event_stats( result: HandshakeResult, handshake_info: HandshakeInfo, ) -> NetworkDnsHandshakeReported64 fn construct_handshake_event_stats(
65     result: HandshakeResult,
66     handshake_info: HandshakeInfo,
67 ) -> NetworkDnsHandshakeReported {
68     let mut handshake_event_atom = create_default_handshake_atom();
69     handshake_event_atom.protocol = StatsdProtocol::ProtoDoh;
70     handshake_event_atom.result = match result {
71         HandshakeResult::Success => StatsdResult::HrSuccess,
72         HandshakeResult::Timeout => StatsdResult::HrTimeout,
73         _ => StatsdResult::HrUnknown,
74     };
75     handshake_event_atom.cause = match handshake_info.cause {
76         Cause::Probe => StatsdCause::HcServerProbe,
77         Cause::Reconnect => StatsdCause::HcReconnectAfterIdle,
78         Cause::Retry => StatsdCause::HcRetryAfterError,
79     };
80     handshake_event_atom.network_type = match handshake_info.network_type {
81         CELLULAR => StatsdNetworkType::NtCellular,
82         WIFI => StatsdNetworkType::NtWifi,
83         BLUETOOTH => StatsdNetworkType::NtBluetooth,
84         ETHERNET => StatsdNetworkType::NtEthernet,
85         VPN => StatsdNetworkType::NtVpn,
86         WIFI_AWARE => StatsdNetworkType::NtWifiAware,
87         LOWPAN => StatsdNetworkType::NtLowpan,
88         CELLULAR_VPN => StatsdNetworkType::NtCellularVpn,
89         WIFI_VPN => StatsdNetworkType::NtWifiVpn,
90         BLUETOOTH_VPN => StatsdNetworkType::NtBluetoothVpn,
91         ETHERNET_VPN => StatsdNetworkType::NtEthernetVpn,
92         WIFI_CELLULAR_VPN => StatsdNetworkType::NtWifiCellularVpn,
93         _ => StatsdNetworkType::NtUnknown,
94     };
95     handshake_event_atom.private_dns_mode = match handshake_info.private_dns_mode {
96         OFF => StatsdPrivateDnsMode::PdmOff,
97         OPPORTUNISTIC => StatsdPrivateDnsMode::PdmOpportunistic,
98         STRICT => StatsdPrivateDnsMode::PdmStrict,
99         _ => StatsdPrivateDnsMode::PdmUnknown,
100     };
101     handshake_event_atom.latency_micros = handshake_info.elapsed as i32;
102     handshake_event_atom.bytes_sent = handshake_info.sent_bytes as i32;
103     handshake_event_atom.bytes_received = handshake_info.recv_bytes as i32;
104     handshake_event_atom.tls_session_cache_hit = handshake_info.session_hit_checker;
105     handshake_event_atom.tls_version = TLS1_3_VERSION as i32;
106     handshake_event_atom.hostname_verification = matches!(handshake_info.private_dns_mode, STRICT);
107     handshake_event_atom.quic_version = handshake_info.quic_version as i32;
108     handshake_event_atom
109 }
110 
111 /// Log hankshake events via statsd API.
log_handshake_event_stats(result: HandshakeResult, handshake_info: HandshakeInfo)112 pub fn log_handshake_event_stats(result: HandshakeResult, handshake_info: HandshakeInfo) {
113     let handshake_event_stats = construct_handshake_event_stats(result, handshake_info);
114 
115     let logging_result = handshake_event_stats.stats_write();
116     if let Err(e) = logging_result {
117         log::error!("Error in logging handshake event. {:?}", e);
118     }
119 }
120 
121 #[cfg(test)]
122 mod tests {
123     use super::*;
124 
125     #[test]
test_metrics_write()126     fn test_metrics_write() {
127         let handshake_info = HandshakeInfo {
128             cause: Cause::Retry,
129             network_type: WIFI,
130             private_dns_mode: STRICT,
131             elapsed: 42596,
132             sent_bytes: 761,
133             recv_bytes: 6420,
134             session_hit_checker: false,
135             quic_version: 1,
136         };
137         let result = HandshakeResult::Timeout;
138         let handshake_event_stats = construct_handshake_event_stats(result, handshake_info);
139         assert_eq!(handshake_event_stats.protocol as i32, StatsdProtocol::ProtoDoh as i32);
140         assert_eq!(handshake_event_stats.result as i32, HandshakeResult::Timeout as i32);
141         assert_eq!(handshake_event_stats.cause as i32, StatsdCause::HcRetryAfterError as i32);
142         assert_eq!(handshake_event_stats.network_type as i32, StatsdNetworkType::NtWifi as i32);
143         assert_eq!(
144             handshake_event_stats.private_dns_mode as i32,
145             StatsdPrivateDnsMode::PdmStrict as i32
146         );
147         assert_eq!(handshake_event_stats.latency_micros, 42596);
148         assert_eq!(handshake_event_stats.bytes_sent, 761);
149         assert_eq!(handshake_event_stats.bytes_received, 6420);
150         assert_eq!(handshake_event_stats.round_trips, -1);
151         assert!(!handshake_event_stats.tls_session_cache_hit);
152         assert!(handshake_event_stats.hostname_verification);
153         assert_eq!(handshake_event_stats.quic_version, 1);
154         assert_eq!(handshake_event_stats.server_index, -1);
155         assert_eq!(handshake_event_stats.sampling_rate_denom, -1);
156     }
157 }
158