1use anyhow::{Context as _, Error, format_err};
6use fidl::endpoints;
7use fidl_fuchsia_wlan_common::WlanMacRole;
8use fidl_fuchsia_wlan_device_service::DeviceMonitorProxy;
9use futures::stream::TryStreamExt;
10use ieee80211::Ssid;
11use wlan_common::bss::{BssDescription, Protection};
12use wlan_common::security::SecurityError;
13use wlan_common::security::wep::WepKey;
14use wlan_common::security::wpa::credential::{Passphrase, Psk};
15use {
16 fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_common_security as fidl_security,
17 fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_sme as fidl_sme,
18};
19
20type WlanService = DeviceMonitorProxy;
21
22#[derive(Clone, Debug)]
27struct SecurityContext {
28 pub bss: BssDescription,
29 pub unparsed_credential_bytes: Vec<u8>,
30}
31
32impl TryFrom<SecurityContext> for fidl_security::Authentication {
39 type Error = SecurityError;
40
41 fn try_from(context: SecurityContext) -> Result<Self, SecurityError> {
42 let SecurityContext { bss, unparsed_credential_bytes } = context;
43 fn parse_wpa_credentials(
44 unparsed_credential_bytes: Vec<u8>,
45 ) -> Result<fidl_security::Credentials, SecurityError> {
46 if let Ok(psk) = Psk::try_from(unparsed_credential_bytes.as_slice()) {
48 Ok(fidl_security::Credentials::Wpa(fidl_security::WpaCredentials::Psk(psk.into())))
49 } else {
50 let passphrase = Passphrase::try_from(unparsed_credential_bytes)?;
51 Ok(fidl_security::Credentials::Wpa(fidl_security::WpaCredentials::Passphrase(
52 passphrase.into(),
53 )))
54 }
55 }
56
57 match bss.protection() {
58 Protection::Unknown | Protection::Wpa2Enterprise | Protection::Wpa3Enterprise => {
61 Err(SecurityError::Unsupported)
62 }
63 Protection::Open | Protection::Owe | Protection::OpenOweTransition
64 if unparsed_credential_bytes.len() > 0 =>
65 {
66 Err(SecurityError::Incompatible)
67 }
68 Protection::Open | Protection::OpenOweTransition => Ok(fidl_security::Authentication {
69 protocol: fidl_security::Protocol::Open,
70 credentials: None,
71 }),
72 Protection::Owe => Ok(fidl_security::Authentication {
73 protocol: fidl_security::Protocol::Owe,
74 credentials: None,
75 }),
76 Protection::Wep => WepKey::parse(unparsed_credential_bytes.as_slice())
77 .map(|key| fidl_security::Authentication {
78 protocol: fidl_security::Protocol::Wep,
79 credentials: Some(Box::new(fidl_security::Credentials::Wep(
80 fidl_security::WepCredentials { key: key.into() },
81 ))),
82 })
83 .map_err(From::from),
84 Protection::Wpa1 => {
85 parse_wpa_credentials(unparsed_credential_bytes).map(|credentials| {
86 fidl_security::Authentication {
87 protocol: fidl_security::Protocol::Wpa1,
88 credentials: Some(Box::new(credentials)),
89 }
90 })
91 }
92 Protection::Wpa1Wpa2PersonalTkipOnly
93 | Protection::Wpa1Wpa2Personal
94 | Protection::Wpa2PersonalTkipOnly
95 | Protection::Wpa2Personal => {
96 parse_wpa_credentials(unparsed_credential_bytes).map(|credentials| {
97 fidl_security::Authentication {
98 protocol: fidl_security::Protocol::Wpa2Personal,
99 credentials: Some(Box::new(credentials)),
100 }
101 })
102 }
103 Protection::Wpa2Wpa3Personal => {
104 if let Ok(psk) = Psk::try_from(unparsed_credential_bytes.as_slice()) {
106 Ok(fidl_security::Authentication {
107 protocol: fidl_security::Protocol::Wpa2Personal,
108 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
109 fidl_security::WpaCredentials::Psk(psk.into()),
110 ))),
111 })
112 } else {
113 Passphrase::try_from(unparsed_credential_bytes)
114 .map(|passphrase| fidl_security::Authentication {
115 protocol: fidl_security::Protocol::Wpa3Personal,
116 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
117 fidl_security::WpaCredentials::Passphrase(passphrase.into()),
118 ))),
119 })
120 .map_err(From::from)
121 }
122 }
123 Protection::Wpa3Personal => Passphrase::try_from(unparsed_credential_bytes)
124 .map(|passphrase| fidl_security::Authentication {
125 protocol: fidl_security::Protocol::Wpa3Personal,
126 credentials: Some(Box::new(fidl_security::Credentials::Wpa(
127 fidl_security::WpaCredentials::Passphrase(passphrase.into()),
128 ))),
129 })
130 .map_err(From::from),
131 }
132 }
133}
134
135pub async fn get_sme_proxy(
136 wlan_svc: &WlanService,
137 iface_id: u16,
138) -> Result<fidl_sme::ClientSmeProxy, Error> {
139 let (sme_proxy, sme_remote) = endpoints::create_proxy();
140 let result = wlan_svc
141 .get_client_sme(iface_id, sme_remote)
142 .await
143 .context("error sending GetClientSme request")?;
144 match result {
145 Ok(()) => Ok(sme_proxy),
146 Err(e) => Err(format_err!(
147 "Failed to get client sme proxy for interface id {} with error {}",
148 iface_id,
149 e
150 )),
151 }
152}
153
154pub async fn get_first_sme(wlan_svc: &WlanService) -> Result<fidl_sme::ClientSmeProxy, Error> {
155 let iface_id = super::get_first_iface(wlan_svc, WlanMacRole::Client)
156 .await
157 .context("failed to get iface")?;
158 get_sme_proxy(&wlan_svc, iface_id).await
159}
160
161pub async fn connect(
162 iface_sme_proxy: &fidl_sme::ClientSmeProxy,
163 target_ssid: Ssid,
164 target_pwd: Vec<u8>,
165 target_bss_desc: fidl_common::BssDescription,
166) -> Result<bool, Error> {
167 let (connection_proxy, connection_remote) = endpoints::create_proxy();
168
169 let authentication = fidl_security::Authentication::try_from(SecurityContext {
173 bss: BssDescription::try_from(target_bss_desc.clone())?,
174 unparsed_credential_bytes: target_pwd,
175 })?;
176 let req = fidl_sme::ConnectRequest {
177 ssid: target_ssid.clone().into(),
178 bss_description: target_bss_desc,
179 authentication,
180 deprecated_scan_type: fidl_common::ScanType::Passive,
181 multiple_bss_candidates: false, };
183
184 let _result = iface_sme_proxy.connect(&req, Some(connection_remote))?;
185
186 let connection_result_code = handle_connect_transaction(connection_proxy).await?;
187
188 if !matches!(connection_result_code, fidl_ieee80211::StatusCode::Success) {
189 log::error!("Failed to connect to network: {:?}", connection_result_code);
190 return Ok(false);
191 }
192
193 let client_status_response =
194 iface_sme_proxy.status().await.context("failed to check status from sme_proxy")?;
195 Ok(match client_status_response {
196 fidl_sme::ClientStatusResponse::Connected(serving_ap_info) => {
197 if serving_ap_info.ssid != target_ssid {
198 log::error!(
199 "Connected to wrong network: {:?}. Expected: {:?}.",
200 serving_ap_info.ssid.as_slice(),
201 target_ssid
202 );
203 false
204 } else {
205 true
206 }
207 }
208 fidl_sme::ClientStatusResponse::Connecting(_)
209 | fidl_sme::ClientStatusResponse::Roaming(_)
210 | fidl_sme::ClientStatusResponse::Idle(_) => {
211 log::error!(
212 "Unexpected status {:?} after {:?}",
213 client_status_response,
214 connection_result_code
215 );
216 false
217 }
218 })
219}
220
221async fn handle_connect_transaction(
222 connect_transaction: fidl_sme::ConnectTransactionProxy,
223) -> Result<fidl_ieee80211::StatusCode, Error> {
224 let mut event_stream = connect_transaction.take_event_stream();
225 let mut result_code = fidl_ieee80211::StatusCode::RefusedReasonUnspecified;
226
227 if let Some(evt) = event_stream
228 .try_next()
229 .await
230 .context("failed to receive connect result before the channel was closed")?
231 {
232 match evt {
233 fidl_sme::ConnectTransactionEvent::OnConnectResult { result } => {
234 result_code = result.code;
235 }
236 other => {
237 return Err(format_err!(
238 "Expected ConnectTransactionEvent::OnConnectResult event, got {:?}",
239 other
240 ));
241 }
242 }
243 }
244
245 Ok(result_code)
246}
247
248pub async fn disconnect(iface_sme_proxy: &fidl_sme::ClientSmeProxy) -> Result<(), Error> {
249 iface_sme_proxy
250 .disconnect(fidl_sme::UserDisconnectReason::WlanServiceUtilTesting)
251 .await
252 .context("failed to trigger disconnect")?;
253
254 let client_status_response =
256 iface_sme_proxy.status().await.context("failed to check status from sme_proxy")?;
257 match client_status_response {
258 fidl_sme::ClientStatusResponse::Connected(_)
259 | fidl_sme::ClientStatusResponse::Connecting(_)
260 | fidl_sme::ClientStatusResponse::Roaming(_) => {
261 Err(format_err!("Disconnect confirmation failed: {:?}", client_status_response))
262 }
263 fidl_sme::ClientStatusResponse::Idle(_) => Ok(()),
264 }
265}
266
267pub async fn disconnect_all(wlan_svc: &WlanService) -> Result<(), Error> {
268 let wlan_iface_ids =
269 super::get_iface_list(wlan_svc).await.context("Connect: failed to get wlan iface list")?;
270
271 let mut error_msg = format!("");
272 for iface_id in wlan_iface_ids {
273 let result = wlan_svc.query_iface(iface_id).await.context("querying iface info")?;
274
275 match result {
276 Ok(query_info) => {
277 if query_info.role == WlanMacRole::Client {
278 let sme_proxy = get_sme_proxy(&wlan_svc, iface_id)
279 .await
280 .context("Disconnect all: failed to get iface sme proxy")?;
281 if let Err(e) = disconnect(&sme_proxy).await {
282 error_msg =
283 format!("{}Error disconnecting iface {}: {}\n", error_msg, iface_id, e);
284 log::error!("disconnect_all: disconnect err on iface {}: {}", iface_id, e);
285 }
286 }
287 }
288 Err(zx::sys::ZX_ERR_NOT_FOUND) => {
289 error_msg = format!("{}no query response on iface {}\n", error_msg, iface_id);
290 log::error!("disconnect_all: iface query empty on iface {}", iface_id);
291 }
292 Err(e) => {
293 error_msg = format!("{}failed querying iface {}: {}\n", error_msg, iface_id, e);
294 log::error!("disconnect_all: query err on iface {}: {}", iface_id, e);
295 }
296 }
297 }
298 if error_msg.is_empty() { Ok(()) } else { Err(format_err!("{}", error_msg)) }
299}
300
301pub async fn passive_scan(
302 iface_sme_proxy: &fidl_sme::ClientSmeProxy,
303) -> Result<Vec<fidl_sme::ScanResult>, Error> {
304 let vmo = iface_sme_proxy
305 .scan(&fidl_sme::ScanRequest::Passive(fidl_sme::PassiveScanRequest {}))
306 .await
307 .context("error sending scan request")?
308 .map_err(|scan_error_code| format_err!("Scan error: {:?}", scan_error_code))?;
309 wlan_common::scan::read_vmo(vmo)
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315 use crate::*;
316 use fidl::endpoints::RequestStream;
317 use fidl_fuchsia_wlan_device_service::{
318 self as wlan_service, DeviceMonitorMarker, DeviceMonitorProxy, DeviceMonitorRequest,
319 DeviceMonitorRequestStream,
320 };
321 use fidl_fuchsia_wlan_sme::{
322 ClientSmeMarker, ClientSmeRequest, ClientSmeRequestStream, Protection,
323 };
324 use fuchsia_async::TestExecutor;
325 use futures::stream::{StreamExt, StreamFuture};
326 use futures::task::Poll;
327 use ieee80211::Ssid;
328 use rand::Rng as _;
329 use std::convert::{TryFrom, TryInto};
330 use std::pin::pin;
331 use wlan_common::channel::{Cbw, Channel};
332 use wlan_common::scan::write_vmo;
333 use wlan_common::{assert_variant, fake_fidl_bss_description};
334 use {
335 fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_common_security as fidl_security,
336 };
337
338 fn generate_random_wpa2_bss_description() -> fidl_fuchsia_wlan_internal::BssDescription {
339 let mut rng = rand::rng();
340 fidl_fuchsia_wlan_internal::BssDescription {
341 bssid: (0..6).map(|_| rng.r#gen::<u8>()).collect::<Vec<u8>>().try_into().unwrap(),
342 beacon_period: rng.r#gen::<u16>(),
343 rssi_dbm: rng.r#gen::<i8>(),
344 channel: fidl_ieee80211::WlanChannel {
345 primary: rng.r#gen::<u8>(),
346 cbw: match rng.r#gen_range(0..5) {
347 0 => fidl_ieee80211::ChannelBandwidth::Cbw20,
348 1 => fidl_ieee80211::ChannelBandwidth::Cbw40,
349 2 => fidl_ieee80211::ChannelBandwidth::Cbw40Below,
350 3 => fidl_ieee80211::ChannelBandwidth::Cbw80,
351 4 => fidl_ieee80211::ChannelBandwidth::Cbw160,
352 5 => fidl_ieee80211::ChannelBandwidth::Cbw80P80,
353 _ => panic!(),
354 },
355 secondary80: rng.r#gen::<u8>(),
356 },
357 snr_db: rng.r#gen::<i8>(),
358 ..fake_fidl_bss_description!(Wpa2)
359 }
360 }
361
362 fn extract_sme_server_from_get_client_sme_req_and_respond(
363 exec: &mut TestExecutor,
364 req_stream: &mut DeviceMonitorRequestStream,
365 result: Result<(), zx::Status>,
366 ) -> fidl_sme::ClientSmeRequestStream {
367 let req = exec.run_until_stalled(&mut req_stream.next());
368
369 let (responder, fake_sme_server) = assert_variant !(
370 req,
371 Poll::Ready(Some(Ok(DeviceMonitorRequest::GetClientSme{ iface_id:_, sme_server, responder})))
372 => (responder, sme_server));
373
374 responder
376 .send(result.map_err(|e| e.into_raw()))
377 .expect("fake sme proxy response: send failed");
378
379 fake_sme_server.into_stream()
383 }
384
385 fn respond_to_get_client_sme_request(
386 exec: &mut TestExecutor,
387 req_stream: &mut DeviceMonitorRequestStream,
388 result: Result<(), zx::Status>,
389 ) {
390 let req = exec.run_until_stalled(&mut req_stream.next());
391
392 let responder = assert_variant !(
393 req,
394 Poll::Ready(Some(Ok(DeviceMonitorRequest::GetClientSme{ responder, ..})))
395 => responder);
396
397 responder
399 .send(result.map_err(|e| e.into_raw()))
400 .expect("fake sme proxy response: send failed")
401 }
402
403 fn respond_to_client_sme_disconnect_request(
404 exec: &mut TestExecutor,
405 req_stream: &mut ClientSmeRequestStream,
406 ) {
407 let req = exec.run_until_stalled(&mut req_stream.next());
408 let responder = assert_variant !(
409 req,
410 Poll::Ready(Some(Ok(ClientSmeRequest::Disconnect{ responder, .. })))
411 => responder);
412
413 responder.send().expect("fake disconnect response: send failed")
415 }
416
417 fn respond_to_client_sme_status_request(
418 exec: &mut TestExecutor,
419 req_stream: &mut ClientSmeRequestStream,
420 status: &StatusResponse,
421 ) {
422 let req = exec.run_until_stalled(&mut req_stream.next());
423 let responder = assert_variant !(
424 req,
425 Poll::Ready(Some(Ok(ClientSmeRequest::Status{ responder})))
426 => responder);
427
428 match status {
430 StatusResponse::Idle => {
431 let response = fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty {});
432 responder.send(&response).expect("Failed to send StatusResponse.");
433 }
434 StatusResponse::Connected => {
435 let serving_ap_info =
436 create_serving_ap_info_using_ssid(Ssid::try_from([1, 2, 3, 4]).unwrap());
437 let response = fidl_sme::ClientStatusResponse::Connected(serving_ap_info);
438 responder.send(&response).expect("Failed to send StatusResponse.");
439 }
440 StatusResponse::Connecting => {
441 let response = fidl_sme::ClientStatusResponse::Connecting(vec![1, 2, 3, 4]);
442 responder.send(&response).expect("Failed to send StatusResponse.");
443 }
444 }
445 }
446
447 fn test_get_first_sme(iface_list: &[WlanMacRole]) -> Result<(), Error> {
448 let (mut exec, proxy, mut req_stream) =
449 crate::tests::setup_fake_service::<DeviceMonitorMarker>();
450 let fut = get_first_sme(&proxy);
451 let mut fut = pin!(fut);
452
453 let ifaces = (0..iface_list.len() as u16).collect();
454
455 assert!(exec.run_until_stalled(&mut fut).is_pending());
456 crate::tests::respond_to_query_iface_list_request(&mut exec, &mut req_stream, ifaces);
457
458 for mac_role in iface_list {
459 assert!(exec.run_until_stalled(&mut fut).is_pending());
461
462 crate::tests::respond_to_query_iface_request(
463 &mut exec,
464 &mut req_stream,
465 *mac_role,
466 Some([1, 2, 3, 4, 5, 6]),
467 );
468
469 if *mac_role == WlanMacRole::Client {
470 assert!(exec.run_until_stalled(&mut fut).is_pending());
472 respond_to_get_client_sme_request(&mut exec, &mut req_stream, Ok(()));
473 break;
474 }
475 }
476
477 let _proxy = exec.run_singlethreaded(&mut fut)?;
478 Ok(())
479 }
480
481 fn test_disconnect_all(iface_list: &[(WlanMacRole, StatusResponse)]) -> Result<(), Error> {
482 let (mut exec, proxy, mut req_stream) =
483 crate::tests::setup_fake_service::<DeviceMonitorMarker>();
484 let fut = disconnect_all(&proxy);
485 let mut fut = pin!(fut);
486
487 let ifaces = (0..iface_list.len() as u16).collect();
488
489 assert!(exec.run_until_stalled(&mut fut).is_pending());
490 crate::tests::respond_to_query_iface_list_request(&mut exec, &mut req_stream, ifaces);
491
492 for (mac_role, status) in iface_list {
493 assert!(exec.run_until_stalled(&mut fut).is_pending());
495 crate::tests::respond_to_query_iface_request(
496 &mut exec,
497 &mut req_stream,
498 *mac_role,
499 Some([1, 2, 3, 4, 5, 6]),
500 );
501
502 if *mac_role == WlanMacRole::Client {
503 assert!(exec.run_until_stalled(&mut fut).is_pending());
505 let mut fake_sme_server_stream =
506 extract_sme_server_from_get_client_sme_req_and_respond(
507 &mut exec,
508 &mut req_stream,
509 Ok(()),
510 );
511
512 assert!(exec.run_until_stalled(&mut fut).is_pending());
514 respond_to_client_sme_disconnect_request(&mut exec, &mut fake_sme_server_stream);
515
516 assert!(exec.run_until_stalled(&mut fut).is_pending());
517
518 respond_to_client_sme_status_request(
520 &mut exec,
521 &mut fake_sme_server_stream,
522 status,
523 );
524 }
525 }
526 exec.run_singlethreaded(&mut fut)
527 }
528
529 #[test]
531 fn check_get_client_sme_success() {
532 let iface_list: Vec<WlanMacRole> = vec![WlanMacRole::Ap, WlanMacRole::Client];
533 test_get_first_sme(&iface_list).expect("expect success but failed");
534 }
535
536 #[test]
538 fn check_get_client_sme_no_devices() {
539 let iface_list: Vec<WlanMacRole> = Vec::new();
540 test_get_first_sme(&iface_list).expect_err("expect fail but succeeded");
541 }
542
543 #[test]
545 fn check_get_client_sme_no_clients() {
546 let iface_list: Vec<WlanMacRole> = vec![WlanMacRole::Ap, WlanMacRole::Ap];
547 test_get_first_sme(&iface_list).expect_err("expect fail but succeeded");
548 }
549
550 #[test]
553 fn check_disconnect_all_client_and_ap_success() {
554 let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
555 (WlanMacRole::Ap, StatusResponse::Idle),
556 (WlanMacRole::Client, StatusResponse::Idle),
557 ];
558 test_disconnect_all(&iface_list).expect("Expect success but failed")
559 }
560
561 #[test]
564 fn check_disconnect_all_all_clients_success() {
565 let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
566 (WlanMacRole::Client, StatusResponse::Idle),
567 (WlanMacRole::Client, StatusResponse::Idle),
568 ];
569 test_disconnect_all(&iface_list).expect("Expect success but failed");
570 }
571
572 #[test]
574 fn check_disconnect_all_all_clients_fail() {
575 let iface_list: Vec<(WlanMacRole, StatusResponse)> = vec![
576 (WlanMacRole::Ap, StatusResponse::Connected),
577 (WlanMacRole::Client, StatusResponse::Connected),
578 ];
579 test_disconnect_all(&iface_list).expect_err("Expect fail but succeeded");
580 }
581
582 #[test]
584 fn check_disconnect_all_no_clients_success() {
585 let iface_list: Vec<(WlanMacRole, StatusResponse)> =
586 vec![(WlanMacRole::Ap, StatusResponse::Idle), (WlanMacRole::Ap, StatusResponse::Idle)];
587 test_disconnect_all(&iface_list).expect("Expect success but failed");
588 }
589
590 #[test]
591 fn list_ifaces_returns_iface_id_vector() {
592 let mut exec = TestExecutor::new();
593 let (wlan_monitor, server) = create_wlan_monitor_util();
594 let mut next_device_monitor_req = server.into_future();
595
596 let ifaces: Vec<u16> = vec![0, 1, 35, 36];
597
598 let fut = get_iface_list(&wlan_monitor);
599 let mut fut = pin!(fut);
600 assert!(exec.run_until_stalled(&mut fut).is_pending());
601
602 send_iface_list_response(&mut exec, &mut next_device_monitor_req, ifaces.clone());
603
604 let complete = exec.run_until_stalled(&mut fut);
605
606 let list_response = match complete {
607 Poll::Ready(result) => result,
608 _ => panic!("Expected an iface list response"),
609 };
610
611 let response = match list_response {
612 Ok(response) => response,
613 Err(_) => panic!("Expected a valid list response"),
614 };
615
616 assert_eq!(response, ifaces)
618 }
619
620 #[test]
621 fn list_ifaces_properly_handles_zero_ifaces() {
622 let mut exec = TestExecutor::new();
623 let (wlan_monitor, server) = create_wlan_monitor_util();
624 let mut next_device_monitor_req = server.into_future();
625
626 let iface_id_list: Vec<u16> = vec![];
628 let iface_list_vec = vec![];
629
630 let fut = get_iface_list(&wlan_monitor);
631 let mut fut = pin!(fut);
632 assert!(exec.run_until_stalled(&mut fut).is_pending());
633
634 send_iface_list_response(&mut exec, &mut next_device_monitor_req, iface_list_vec);
635
636 let complete = exec.run_until_stalled(&mut fut);
637
638 let list_response = match complete {
639 Poll::Ready(result) => result,
640 _ => panic!("Expected an iface list response"),
641 };
642
643 let response = match list_response {
644 Ok(response) => response,
645 Err(_) => panic!("Expected a valid list response"),
646 };
647
648 assert_eq!(response, iface_id_list)
650 }
651
652 #[test]
653 fn list_phys_returns_iface_id_vector() {
654 let mut exec = TestExecutor::new();
655 let (monitor_service, server) = create_wlan_monitor_util();
656 let mut next_device_service_req = server.into_future();
657
658 let phy_id_list: Vec<u16> = vec![0, 1, 35, 36];
660 let mut phy_list_vec = vec![];
661 for id in &phy_id_list {
662 phy_list_vec.push(*id);
663 }
664
665 let fut = get_phy_list(&monitor_service);
666 let mut fut = pin!(fut);
667 assert!(exec.run_until_stalled(&mut fut).is_pending());
668
669 send_phy_list_response(&mut exec, &mut next_device_service_req, phy_list_vec);
670
671 let complete = exec.run_until_stalled(&mut fut);
672
673 let list_response = match complete {
674 Poll::Ready(result) => result,
675 _ => panic!("Expected an phy list response"),
676 };
677
678 let response = match list_response {
679 Ok(response) => response,
680 Err(_) => panic!("Expected a valid list response"),
681 };
682
683 assert_eq!(response, phy_id_list)
685 }
686
687 #[test]
688 fn list_phys_properly_handles_zero_phys() {
689 let mut exec = TestExecutor::new();
690 let (monitor_service, server) = create_wlan_monitor_util();
691 let mut next_device_service_req = server.into_future();
692
693 let phy_id_list: Vec<u16> = vec![];
695 let phy_list_vec = vec![];
696
697 let fut = get_phy_list(&monitor_service);
698 let mut fut = pin!(fut);
699 assert!(exec.run_until_stalled(&mut fut).is_pending());
700
701 send_phy_list_response(&mut exec, &mut next_device_service_req, phy_list_vec);
702
703 let complete = exec.run_until_stalled(&mut fut);
704
705 let list_response = match complete {
706 Poll::Ready(result) => result,
707 _ => panic!("Expected an phy list response"),
708 };
709
710 let response = match list_response {
711 Ok(response) => response,
712 Err(_) => panic!("Expected a valid list response"),
713 };
714
715 assert_eq!(response, phy_id_list)
717 }
718
719 fn poll_device_monitor_req(
720 exec: &mut TestExecutor,
721 next_device_monitor_req: &mut StreamFuture<DeviceMonitorRequestStream>,
722 ) -> Poll<DeviceMonitorRequest> {
723 exec.run_until_stalled(next_device_monitor_req).map(|(req, stream)| {
724 *next_device_monitor_req = stream.into_future();
725 req.expect("did not expect the DeviceMonitorRequestStream to end")
726 .expect("error polling device service request stream")
727 })
728 }
729
730 fn send_iface_list_response(
731 exec: &mut TestExecutor,
732 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
733 ifaces: Vec<u16>,
734 ) {
735 let responder = match poll_device_monitor_req(exec, server) {
736 Poll::Ready(DeviceMonitorRequest::ListIfaces { responder }) => responder,
737 Poll::Pending => panic!("expected a request to be available"),
738 _ => panic!("expected a ListIfaces request"),
739 };
740
741 let _result = responder.send(&ifaces[..]);
743 }
744
745 fn send_phy_list_response(
746 exec: &mut TestExecutor,
747 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
748 phy_list_vec: Vec<u16>,
749 ) {
750 let responder = match poll_device_monitor_req(exec, server) {
751 Poll::Ready(DeviceMonitorRequest::ListPhys { responder }) => responder,
752 Poll::Pending => panic!("expected a request to be available"),
753 _ => panic!("expected a ListPhys request"),
754 };
755
756 let _result = responder.send(&phy_list_vec);
758 }
759
760 #[test]
761 fn get_client_sme_valid_iface() {
762 let mut exec = TestExecutor::new();
763 let (wlan_monitor, server) = create_wlan_monitor_util();
764 let mut next_device_monitor_req = server.into_future();
765
766 let fut = get_sme_proxy(&wlan_monitor, 1);
767 let mut fut = pin!(fut);
768 assert!(exec.run_until_stalled(&mut fut).is_pending());
769
770 send_sme_proxy_response(&mut exec, &mut next_device_monitor_req, Ok(()));
772
773 let () = match exec.run_until_stalled(&mut fut) {
774 Poll::Ready(Ok(_)) => (),
775 _ => panic!("Expected a status response"),
776 };
777 }
778
779 fn send_sme_proxy_response(
780 exec: &mut TestExecutor,
781 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
782 result: Result<(), zx::Status>,
783 ) {
784 let responder = match poll_device_monitor_req(exec, server) {
785 Poll::Ready(DeviceMonitorRequest::GetClientSme { responder, .. }) => responder,
786 Poll::Pending => panic!("expected a request to be available"),
787 _ => panic!("expected a GetClientSme request"),
788 };
789
790 let _result = responder.send(result.map_err(|e| e.into_raw()));
792 }
793
794 #[test]
795 fn get_client_sme_invalid_iface() {
796 let mut exec = TestExecutor::new();
797 let (wlan_monitor, server) = create_wlan_monitor_util();
798 let mut next_device_monitor_req = server.into_future();
799
800 let fut = get_sme_proxy(&wlan_monitor, 1);
801 let mut fut = pin!(fut);
802 assert!(exec.run_until_stalled(&mut fut).is_pending());
803
804 send_sme_proxy_response(
806 &mut exec,
807 &mut next_device_monitor_req,
808 Err(zx::Status::NOT_FOUND),
809 );
810
811 let complete = exec.run_until_stalled(&mut fut);
812
813 match complete {
814 Poll::Ready(Err(_)) => (),
815 _ => panic!("Expected a status response"),
816 };
817 }
818
819 #[test]
820 fn connect_success_returns_true() {
821 let connect_result =
822 test_wpa2_connect("TestAp", "password", "TestAp", fidl_ieee80211::StatusCode::Success);
823 assert!(connect_result);
824 }
825
826 #[test]
827 fn connect_failed_returns_false() {
828 let connect_result = test_wpa2_connect(
829 "TestAp",
830 "password",
831 "",
832 fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
833 );
834 assert!(!connect_result);
835 }
836
837 #[test]
838 fn connect_different_ssid_returns_false() {
839 let connect_result = test_wpa2_connect(
840 "TestAp1",
841 "password",
842 "TestAp2",
843 fidl_ieee80211::StatusCode::Success,
844 );
845 assert!(!connect_result);
846 }
847
848 fn test_wpa2_connect(
849 target_ssid: &str,
850 password: &str,
851 connected_to_ssid: &str,
852 result_code: fidl_ieee80211::StatusCode,
853 ) -> bool {
854 let target_ssid = Ssid::try_from(target_ssid).unwrap();
855 let connected_to_ssid = Ssid::try_from(connected_to_ssid).unwrap();
856
857 let mut exec = TestExecutor::new();
858 let (client_sme, server) = create_client_sme_proxy();
859 let mut next_client_sme_req = server.into_future();
860
861 let target_password = password.as_bytes();
862 let target_bss_desc = generate_random_wpa2_bss_description();
863
864 let fut = connect(
865 &client_sme,
866 target_ssid.clone(),
867 target_password.to_vec(),
868 target_bss_desc.clone(),
869 );
870 let mut fut = pin!(fut);
871 assert!(exec.run_until_stalled(&mut fut).is_pending());
872
873 send_connect_request_response(
875 &mut exec,
876 &mut next_client_sme_req,
877 &target_ssid,
878 fidl_security::Authentication::try_from(SecurityContext {
879 bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
880 unparsed_credential_bytes: target_password.to_vec(),
881 })
882 .unwrap(),
883 result_code,
884 );
885
886 if result_code == fidl_ieee80211::StatusCode::Success {
888 assert!(exec.run_until_stalled(&mut fut).is_pending());
889 send_status_response(
890 &mut exec,
891 &mut next_client_sme_req,
892 Some(connected_to_ssid),
893 None,
894 );
895 }
896
897 let complete = exec.run_until_stalled(&mut fut);
898
899 let connection_result = match complete {
900 Poll::Ready(result) => result,
901 _ => panic!("Expected a connect response"),
902 };
903
904 let returned_bool = match connection_result {
905 Ok(response) => response,
906 _ => panic!("Expected a valid connection result"),
907 };
908
909 returned_bool
910 }
911
912 #[test]
913 fn connect_properly_passes_network_info_with_password() {
914 let mut exec = TestExecutor::new();
915 let (client_sme, server) = create_client_sme_proxy();
916 let mut next_client_sme_req = server.into_future();
917
918 let target_ssid = Ssid::try_from("TestAp").unwrap();
919 let target_password = "password".as_bytes();
920 let target_bss_desc = generate_random_wpa2_bss_description();
921
922 let fut = connect(
923 &client_sme,
924 target_ssid.clone(),
925 target_password.to_vec(),
926 target_bss_desc.clone(),
927 );
928 let mut fut = pin!(fut);
929 assert!(exec.run_until_stalled(&mut fut).is_pending());
930
931 verify_connect_request_info(
933 &mut exec,
934 &mut next_client_sme_req,
935 &target_ssid,
936 fidl_security::Authentication::try_from(SecurityContext {
937 bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
938 unparsed_credential_bytes: target_password.to_vec(),
939 })
940 .unwrap(),
941 target_bss_desc,
942 );
943 }
944
945 #[test]
946 fn connect_properly_passes_network_info_open() {
947 let mut exec = TestExecutor::new();
948 let (client_sme, server) = create_client_sme_proxy();
949 let mut next_client_sme_req = server.into_future();
950
951 let target_ssid = Ssid::try_from("TestAp").unwrap();
952 let target_password = "".as_bytes();
953 let target_bss_desc = fake_fidl_bss_description!(Open);
954
955 let fut = connect(
956 &client_sme,
957 target_ssid.clone(),
958 target_password.to_vec(),
959 target_bss_desc.clone(),
960 );
961 let mut fut = pin!(fut);
962 assert!(exec.run_until_stalled(&mut fut).is_pending());
963
964 verify_connect_request_info(
966 &mut exec,
967 &mut next_client_sme_req,
968 &target_ssid,
969 fidl_security::Authentication::try_from(SecurityContext {
970 bss: BssDescription::try_from(target_bss_desc.clone()).unwrap(),
971 unparsed_credential_bytes: vec![],
972 })
973 .unwrap(),
974 target_bss_desc,
975 );
976 }
977
978 fn verify_connect_request_info(
979 exec: &mut TestExecutor,
980 server: &mut StreamFuture<ClientSmeRequestStream>,
981 expected_ssid: &Ssid,
982 expected_authentication: fidl_security::Authentication,
983 expected_bss_desc: fidl_common::BssDescription,
984 ) {
985 match poll_client_sme_request(exec, server) {
986 Poll::Ready(ClientSmeRequest::Connect { req, .. }) => {
987 assert_eq!(expected_ssid, &req.ssid);
988 assert_eq!(req.authentication, expected_authentication);
989 assert_eq!(req.bss_description, expected_bss_desc);
990 }
991 _ => panic!("expected a Connect request"),
992 }
993 }
994
995 fn send_connect_request_response(
996 exec: &mut TestExecutor,
997 server: &mut StreamFuture<ClientSmeRequestStream>,
998 expected_ssid: &Ssid,
999 expected_authentication: fidl_security::Authentication,
1000 connect_result: fidl_ieee80211::StatusCode,
1001 ) {
1002 let responder = match poll_client_sme_request(exec, server) {
1003 Poll::Ready(ClientSmeRequest::Connect { req, txn, .. }) => {
1004 assert_eq!(expected_ssid, &req.ssid[..]);
1005 assert_eq!(req.authentication, expected_authentication);
1006 txn.expect("expected a Connect transaction channel")
1007 }
1008 Poll::Pending => panic!("expected a request to be available"),
1009 _ => panic!("expected a Connect request"),
1010 };
1011 let connect_transaction = responder
1012 .into_stream()
1013 .expect("failed to create a connect transaction stream")
1014 .control_handle();
1015 connect_transaction
1016 .send_on_connect_result(&fidl_sme::ConnectResult {
1017 code: connect_result,
1018 is_credential_rejected: false,
1019 is_reconnect: false,
1020 })
1021 .expect("failed to send OnConnectResult to ConnectTransaction");
1022 }
1023
1024 fn poll_client_sme_request(
1025 exec: &mut TestExecutor,
1026 next_client_sme_req: &mut StreamFuture<ClientSmeRequestStream>,
1027 ) -> Poll<ClientSmeRequest> {
1028 exec.run_until_stalled(next_client_sme_req).map(|(req, stream)| {
1029 *next_client_sme_req = stream.into_future();
1030 req.expect("did not expect the ClientSmeRequestStream to end")
1031 .expect("error polling client sme request stream")
1032 })
1033 }
1034
1035 fn create_client_sme_proxy() -> (fidl_sme::ClientSmeProxy, ClientSmeRequestStream) {
1036 let (proxy, server) = endpoints::create_proxy::<ClientSmeMarker>();
1037 let server = server.into_stream();
1038 (proxy, server)
1039 }
1040
1041 fn create_wlan_monitor_util() -> (DeviceMonitorProxy, DeviceMonitorRequestStream) {
1042 let (proxy, server) = endpoints::create_proxy::<DeviceMonitorMarker>();
1043 let server = server.into_stream();
1044 (proxy, server)
1045 }
1046
1047 enum StatusResponse {
1048 Idle,
1049 Connected,
1050 Connecting,
1051 }
1052
1053 #[test]
1054 fn disconnect_with_empty_status_response() {
1055 if let Poll::Ready(result) = test_disconnect(StatusResponse::Idle) {
1056 return assert!(result.is_ok());
1057 }
1058 panic!("disconnect did not return a Poll::Ready")
1059 }
1060
1061 #[test]
1062 fn disconnect_fail_because_connected() {
1063 if let Poll::Ready(result) = test_disconnect(StatusResponse::Connected) {
1064 return assert!(result.is_err());
1065 }
1066 panic!("disconnect did not return a Poll::Ready")
1067 }
1068
1069 #[test]
1070 fn disconnect_fail_because_connecting() {
1071 if let Poll::Ready(result) = test_disconnect(StatusResponse::Connecting) {
1072 return assert!(result.is_err());
1073 }
1074 panic!("disconnect did not return a Poll::Ready")
1075 }
1076
1077 fn test_disconnect(status: StatusResponse) -> Poll<Result<(), Error>> {
1078 let mut exec = TestExecutor::new();
1079 let (client_sme, server) = create_client_sme_proxy();
1080 let mut client_sme_req = server.into_future();
1081
1082 let fut = disconnect(&client_sme);
1083 let mut fut = pin!(fut);
1084 assert!(exec.run_until_stalled(&mut fut).is_pending());
1085
1086 send_disconnect_request_response(&mut exec, &mut client_sme_req);
1087
1088 assert!(exec.run_until_stalled(&mut fut).is_pending());
1089
1090 match status {
1091 StatusResponse::Idle => {
1092 send_status_response(&mut exec, &mut client_sme_req, None, None)
1093 }
1094 StatusResponse::Connected => send_status_response(
1095 &mut exec,
1096 &mut client_sme_req,
1097 Some(Ssid::try_from([1, 2, 3, 4]).unwrap()),
1098 None,
1099 ),
1100 StatusResponse::Connecting => send_status_response(
1101 &mut exec,
1102 &mut client_sme_req,
1103 None,
1104 Some(Ssid::try_from([1, 2, 3, 4]).unwrap()),
1105 ),
1106 }
1107
1108 exec.run_until_stalled(&mut fut)
1109 }
1110
1111 fn send_disconnect_request_response(
1112 exec: &mut TestExecutor,
1113 server: &mut StreamFuture<ClientSmeRequestStream>,
1114 ) {
1115 let rsp = match poll_client_sme_request(exec, server) {
1116 Poll::Ready(ClientSmeRequest::Disconnect { responder, .. }) => responder,
1117 Poll::Pending => panic!("Expected a DisconnectRequest"),
1118 _ => panic!("Expected a DisconnectRequest"),
1119 };
1120 rsp.send().expect("Failed to send DisconnectResponse.");
1121 }
1122
1123 fn create_serving_ap_info_using_ssid(ssid: Ssid) -> fidl_sme::ServingApInfo {
1124 fidl_sme::ServingApInfo {
1125 bssid: [0, 1, 2, 3, 4, 5],
1126 ssid: ssid.into(),
1127 rssi_dbm: -30,
1128 snr_db: 10,
1129 channel: fidl_ieee80211::WlanChannel {
1130 primary: 1,
1131 cbw: fidl_ieee80211::ChannelBandwidth::Cbw20,
1132 secondary80: 0,
1133 },
1134 protection: Protection::Wpa2Personal,
1135 }
1136 }
1137
1138 fn send_status_response(
1139 exec: &mut TestExecutor,
1140 server: &mut StreamFuture<ClientSmeRequestStream>,
1141 connected_to_ssid: Option<Ssid>,
1142 connecting_to_ssid: Option<Ssid>,
1143 ) {
1144 let rsp = match poll_client_sme_request(exec, server) {
1145 Poll::Ready(ClientSmeRequest::Status { responder }) => responder,
1146 Poll::Pending => panic!("Expected a StatusRequest"),
1147 _ => panic!("Expected a StatusRequest"),
1148 };
1149
1150 let response = match (connected_to_ssid, connecting_to_ssid) {
1151 (Some(_), Some(_)) => panic!("SME cannot simultaneously be Connected and Connecting."),
1152 (Some(ssid), None) => {
1153 let serving_ap_info = create_serving_ap_info_using_ssid(ssid);
1154 fidl_sme::ClientStatusResponse::Connected(serving_ap_info)
1155 }
1156 (None, Some(ssid)) => fidl_sme::ClientStatusResponse::Connecting(ssid.to_vec()),
1157 (None, None) => fidl_sme::ClientStatusResponse::Idle(fidl_sme::Empty {}),
1158 };
1159
1160 rsp.send(&response).expect("Failed to send StatusResponse.");
1161 }
1162
1163 #[test]
1164 fn scan_success_returns_empty_results() {
1165 assert_eq!(test_scan(&[]), &[]);
1166 }
1167
1168 #[test]
1169 fn scan_success_returns_results() {
1170 let scan_results = &[
1171 create_scan_result(
1172 [0, 1, 2, 3, 4, 5],
1173 Ssid::try_from("foo").unwrap(),
1174 -30,
1175 20,
1176 Channel::new(1, Cbw::Cbw20),
1177 Protection::Wpa2Personal,
1178 Some(fidl_sme::Compatibility {
1179 mutual_security_protocols: vec![fidl_security::Protocol::Wpa2Personal],
1180 }),
1181 ),
1182 create_scan_result(
1183 [1, 2, 3, 4, 5, 6],
1184 Ssid::try_from("hello").unwrap(),
1185 -60,
1186 10,
1187 Channel::new(2, Cbw::Cbw20),
1188 Protection::Wpa2Personal,
1189 None,
1190 ),
1191 ];
1192
1193 assert_eq!(test_scan(scan_results), scan_results);
1194 }
1195
1196 #[test]
1197 fn scan_error_correctly_handled() {
1198 assert!(test_scan_error().is_err())
1200 }
1201
1202 fn test_scan(scan_results: &[fidl_sme::ScanResult]) -> Vec<fidl_sme::ScanResult> {
1203 let mut exec = TestExecutor::new();
1204 let (client_sme, server) = create_client_sme_proxy();
1205 let mut client_sme_req = server.into_future();
1206
1207 let fut = passive_scan(&client_sme);
1208 let mut fut = pin!(fut);
1209 assert!(exec.run_until_stalled(&mut fut).is_pending());
1210
1211 send_scan_result_response(&mut exec, &mut client_sme_req, scan_results);
1212
1213 let complete = exec.run_until_stalled(&mut fut);
1214 let request_result = match complete {
1215 Poll::Ready(result) => result,
1216 _ => panic!("Expected a scan request result"),
1217 };
1218 let returned_scan_results = request_result.expect("failed to get scan results");
1219
1220 returned_scan_results
1221 }
1222
1223 fn send_scan_result_response(
1224 exec: &mut TestExecutor,
1225 server: &mut StreamFuture<fidl_sme::ClientSmeRequestStream>,
1226 scan_results: &[fidl_sme::ScanResult],
1227 ) {
1228 match poll_client_sme_request(exec, server) {
1229 Poll::Ready(fidl_sme::ClientSmeRequest::Scan { responder, .. }) => {
1230 let vmo = write_vmo(scan_results.to_vec()).expect("failed to write VMO");
1231 responder.send(Ok(vmo)).expect("failed to send scan results")
1232 }
1233 Poll::Pending => panic!("expected a request to be available"),
1234 _ => panic!("expected a scan request"),
1235 }
1236 }
1237
1238 fn test_scan_error() -> Result<(), Error> {
1239 let mut exec = TestExecutor::new();
1240 let (client_sme, server) = create_client_sme_proxy();
1241 let mut client_sme_req = server.into_future();
1242
1243 let fut = passive_scan(&client_sme);
1244 let mut fut = pin!(fut);
1245 assert!(exec.run_until_stalled(&mut fut).is_pending());
1246
1247 send_scan_error_response(&mut exec, &mut client_sme_req);
1248 let _ = exec.run_until_stalled(&mut fut)?;
1249 Ok(())
1250 }
1251
1252 fn send_scan_error_response(
1253 exec: &mut TestExecutor,
1254 server: &mut StreamFuture<fidl_sme::ClientSmeRequestStream>,
1255 ) {
1256 match poll_client_sme_request(exec, server) {
1257 Poll::Ready(fidl_sme::ClientSmeRequest::Scan { responder, .. }) => responder
1258 .send(Err(fidl_sme::ScanErrorCode::InternalError))
1259 .expect("failed to send ScanError"),
1260 Poll::Pending => panic!("expected a request to be available"),
1261 _ => panic!("expected a scan request"),
1262 };
1263 }
1264
1265 fn create_scan_result(
1266 bssid: [u8; 6],
1267 ssid: Ssid,
1268 rssi_dbm: i8,
1269 snr_db: i8,
1270 channel: Channel,
1271 protection: Protection,
1272 compatibility: Option<fidl_sme::Compatibility>,
1273 ) -> fidl_sme::ScanResult {
1274 fidl_sme::ScanResult {
1275 compatibility: compatibility.map(Box::new),
1276 timestamp_nanos: zx::MonotonicInstant::get().into_nanos(),
1277 bss_description: fake_fidl_bss_description!(
1278 protection => protection,
1279 bssid: bssid,
1280 ssid: ssid,
1281 rssi_dbm: rssi_dbm,
1282 snr_db: snr_db,
1283 channel: channel,
1284 ),
1285 }
1286 }
1287
1288 fn send_destroy_iface_response(
1289 exec: &mut TestExecutor,
1290 server: &mut StreamFuture<wlan_service::DeviceMonitorRequestStream>,
1291 status: zx::Status,
1292 ) {
1293 let responder = match poll_device_monitor_req(exec, server) {
1294 Poll::Ready(DeviceMonitorRequest::DestroyIface { responder, .. }) => responder,
1295 Poll::Pending => panic!("expected a request to be available"),
1296 _ => panic!("expected a destroy iface request"),
1297 };
1298
1299 let _result = responder.send(status.into_raw());
1301 }
1302
1303 #[test]
1304 fn test_destroy_single_iface_ok() {
1305 let mut exec = TestExecutor::new();
1306 let (monitor_service, server) = create_wlan_monitor_util();
1307 let mut next_device_service_req = server.into_future();
1308
1309 let fut = destroy_iface(&monitor_service, 0);
1310 let mut fut = pin!(fut);
1311 assert!(exec.run_until_stalled(&mut fut).is_pending());
1312
1313 send_destroy_iface_response(&mut exec, &mut next_device_service_req, zx::Status::OK);
1314
1315 match exec.run_until_stalled(&mut fut) {
1316 Poll::Ready(Ok(_)) => (),
1317 _ => panic!("Expected a status response"),
1318 };
1319 }
1320}