1use anyhow::{format_err, Error};
6use clap::arg_enum;
7use eui48::MacAddress;
8use structopt::StructOpt;
9use {fidl_fuchsia_wlan_common as wlan_common, fidl_fuchsia_wlan_policy as wlan_policy};
10
11arg_enum! {
12 #[derive(PartialEq, Copy, Clone, Debug)]
13 pub enum RoleArg {
14 Client,
15 Ap
16 }
17}
18
19arg_enum! {
20 #[derive(PartialEq, Copy, Clone, Debug)]
21 pub enum ScanTypeArg {
22 Active,
23 Passive,
24 }
25}
26
27arg_enum! {
28 #[derive(PartialEq, Copy, Clone, Debug)]
29 pub enum SecurityTypeArg {
30 None,
31 Wep,
32 Wpa,
33 Wpa2,
34 Wpa3,
35 }
36}
37
38arg_enum! {
39 #[derive(PartialEq, Copy, Clone, Debug)]
40 pub enum CredentialTypeArg {
41 None,
42 Psk,
43 Password,
44 }
45}
46
47impl From<RoleArg> for wlan_common::WlanMacRole {
48 fn from(arg: RoleArg) -> Self {
49 match arg {
50 RoleArg::Client => wlan_common::WlanMacRole::Client,
51 RoleArg::Ap => wlan_common::WlanMacRole::Ap,
52 }
53 }
54}
55
56impl From<ScanTypeArg> for wlan_common::ScanType {
57 fn from(arg: ScanTypeArg) -> Self {
58 match arg {
59 ScanTypeArg::Active => wlan_common::ScanType::Active,
60 ScanTypeArg::Passive => wlan_common::ScanType::Passive,
61 }
62 }
63}
64
65impl From<SecurityTypeArg> for wlan_policy::SecurityType {
66 fn from(arg: SecurityTypeArg) -> Self {
67 match arg {
68 SecurityTypeArg::r#None => wlan_policy::SecurityType::None,
69 SecurityTypeArg::Wep => wlan_policy::SecurityType::Wep,
70 SecurityTypeArg::Wpa => wlan_policy::SecurityType::Wpa,
71 SecurityTypeArg::Wpa2 => wlan_policy::SecurityType::Wpa2,
72 SecurityTypeArg::Wpa3 => wlan_policy::SecurityType::Wpa3,
73 }
74 }
75}
76
77impl From<PolicyNetworkConfig> for wlan_policy::NetworkConfig {
78 fn from(arg: PolicyNetworkConfig) -> Self {
79 let credential = match arg.credential_type {
80 Some(CredentialTypeArg::r#None) => wlan_policy::Credential::None(wlan_policy::Empty),
81 Some(CredentialTypeArg::Psk) => {
82 wlan_policy::Credential::Psk(parse_psk_string(arg.credential.unwrap()))
83 }
84 Some(CredentialTypeArg::Password) => {
85 wlan_policy::Credential::Password(arg.credential.unwrap().as_bytes().to_vec())
86 }
87 None => {
88 credential_from_string(arg.credential.unwrap_or_else(|| "".to_string()))
90 }
91 };
92
93 let security_type = security_type_from_args(arg.security_type, &credential);
94
95 let network_id = wlan_policy::NetworkIdentifier {
96 ssid: arg.ssid.as_bytes().to_vec(),
97 type_: security_type,
98 };
99 wlan_policy::NetworkConfig {
100 id: Some(network_id),
101 credential: Some(credential),
102 ..Default::default()
103 }
104 }
105}
106
107fn parse_psk_string(credential: String) -> Vec<u8> {
110 let psk_arg = credential.as_bytes().to_vec();
111 hex::decode(psk_arg).expect(
112 "Error: PSK must be 64 hexadecimal characters.\
113 Example: \"123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF1234\"",
114 )
115}
116
117fn credential_from_string(credential: String) -> wlan_policy::Credential {
120 match credential.len() {
121 0 => wlan_policy::Credential::None(wlan_policy::Empty),
122 0..=63 => wlan_policy::Credential::Password(credential.into_bytes()),
123 64 => wlan_policy::Credential::Psk(parse_psk_string(credential)),
124 65..=usize::MAX => {
125 panic!(
126 "Provided credential is too long. A password must be between 0 and 63 \
127 characters and a PSK must be 64 hexadecimal characters. Provided \
128 credential is {} characters.",
129 credential.len()
130 );
131 }
132 _ => {
133 panic!("Invalid credential of length {}", credential.len())
135 }
136 }
137}
138
139fn security_type_from_args(
142 security_arg: Option<SecurityTypeArg>,
143 credential: &wlan_policy::Credential,
144) -> wlan_policy::SecurityType {
145 if let Some(arg) = security_arg {
146 match arg {
147 SecurityTypeArg::Wep => wlan_policy::SecurityType::Wep,
148 SecurityTypeArg::Wpa => wlan_policy::SecurityType::Wpa,
149 SecurityTypeArg::Wpa2 => wlan_policy::SecurityType::Wpa2,
150 SecurityTypeArg::Wpa3 => wlan_policy::SecurityType::Wpa3,
151 SecurityTypeArg::r#None => wlan_policy::SecurityType::None,
152 }
153 } else {
154 match credential {
155 wlan_policy::Credential::None(_) => wlan_policy::SecurityType::None,
156 _ => wlan_policy::SecurityType::Wpa2,
157 }
158 }
159}
160
161#[derive(StructOpt, Clone, Debug)]
162pub struct PolicyNetworkConfig {
163 #[structopt(long, required = true)]
164 pub ssid: String,
165 #[structopt(
166 long = "security-type",
167 possible_values = &SecurityTypeArg::variants(),
168 case_insensitive = true,
169 )]
170 pub security_type: Option<SecurityTypeArg>,
171 #[structopt(
172 long = "credential-type",
173 possible_values = &CredentialTypeArg::variants(),
174 case_insensitive = true,
175 )]
176 pub credential_type: Option<CredentialTypeArg>,
177 #[structopt(long)]
178 pub credential: Option<String>,
179}
180
181#[derive(StructOpt, Clone, Debug)]
192pub struct RemoveArgs {
193 #[structopt(long, required = true)]
194 pub ssid: String,
195 #[structopt(
196 long = "security-type",
197 possible_values = &SecurityTypeArg::variants(),
198 case_insensitive = true,
199 )]
200 pub security_type: Option<SecurityTypeArg>,
201 #[structopt(
202 long = "credential-type",
203 possible_values = &CredentialTypeArg::variants(),
204 case_insensitive = true,
205 )]
206 pub credential_type: Option<CredentialTypeArg>,
207 #[structopt(long)]
208 pub credential: Option<String>,
209}
210
211impl RemoveArgs {
212 pub fn parse_security(&self) -> Option<wlan_policy::SecurityType> {
213 self.security_type.map(|s| s.into())
214 }
215
216 pub fn try_parse_credential(&self) -> Result<Option<wlan_policy::Credential>, Error> {
223 let credential = if let Some(cred_val) = &self.credential {
224 match self.credential_type {
225 Some(CredentialTypeArg::Password) => {
226 Some(wlan_policy::Credential::Password(cred_val.as_bytes().to_vec()))
227 }
228 Some(CredentialTypeArg::Psk) => {
229 let psk_arg = cred_val.as_bytes().to_vec();
232 let psk = hex::decode(psk_arg).expect(
233 "Error: PSK must be 64 hexadecimal characters.\
234 Example: \"123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF1234\"",
235 );
236 Some(wlan_policy::Credential::Psk(psk))
237 }
238 Some(CredentialTypeArg::None) => {
239 if cred_val.is_empty() {
242 Some(wlan_policy::Credential::None(wlan_policy::Empty))
243 } else {
244 return Err(format_err!(
245 "A credential value was provided with a credential \
246 type of None. No value be provided if there is no credential, or the \
247 credential type should be PSK or Password."
248 ));
249 }
250 }
251 None => {
252 if cred_val.is_empty() {
256 Some(wlan_policy::Credential::None(wlan_policy::Empty))
257 } else {
258 Some(wlan_policy::Credential::Password(cred_val.as_bytes().to_vec()))
259 }
260 }
261 }
262 } else {
263 if let Some(credential_type) = self.credential_type {
264 if credential_type == CredentialTypeArg::None {
265 Some(wlan_policy::Credential::None(wlan_policy::Empty))
266 } else {
267 return Err(format_err!(
268 "A credential type was provided with no value. Please \
269 provide the credential to be used"
270 ));
271 }
272 } else {
273 None
277 }
278 };
279 Ok(credential)
280 }
281}
282
283#[derive(StructOpt, Clone, Debug)]
284pub struct SaveNetworkArgs {
285 #[structopt(long, required = true)]
286 pub ssid: String,
287 #[structopt(
288 long = "security-type",
289 possible_values = &SecurityTypeArg::variants(),
290 case_insensitive = true,
291 )]
292 pub security_type: SecurityTypeArg,
293 #[structopt(
294 long = "credential-type",
295 possible_values = &CredentialTypeArg::variants(),
296 case_insensitive = true,
297 )]
298 pub credential_type: Option<CredentialTypeArg>,
299 #[structopt(long)]
300 pub credential: String,
301}
302
303#[derive(StructOpt, Clone, Debug)]
304pub struct ConnectArgs {
305 #[structopt(long, required = true)]
306 pub ssid: String,
307 #[structopt(
308 long = "security-type",
309 possible_values = &SecurityTypeArg::variants(),
310 case_insensitive = true,
311 )]
312 pub security_type: Option<SecurityTypeArg>,
313}
314
315#[derive(StructOpt, Clone, Debug)]
316pub enum PolicyClientCmd {
317 #[structopt(name = "connect")]
318 Connect(ConnectArgs),
319 #[structopt(name = "list-saved-networks")]
320 GetSavedNetworks,
321 #[structopt(name = "listen")]
322 Listen,
323 #[structopt(name = "remove-network")]
324 RemoveNetwork(RemoveArgs),
325 #[structopt(name = "save-network")]
326 SaveNetwork(PolicyNetworkConfig),
327 #[structopt(name = "scan")]
328 ScanForNetworks,
329 #[structopt(name = "start-client-connections")]
330 StartClientConnections,
331 #[structopt(name = "stop-client-connections")]
332 StopClientConnections,
333 #[structopt(name = "dump-config")]
334 DumpConfig,
335 #[structopt(name = "restore-config")]
336 RestoreConfig { serialized_config: String },
337 #[structopt(name = "status")]
338 Status,
339}
340
341#[derive(StructOpt, Clone, Debug)]
342pub enum PolicyAccessPointCmd {
343 #[structopt(name = "start")]
345 Start(PolicyNetworkConfig),
346 #[structopt(name = "stop")]
347 Stop(PolicyNetworkConfig),
348 #[structopt(name = "stop-all")]
349 StopAllAccessPoints,
350 #[structopt(name = "listen")]
351 Listen,
352 #[structopt(name = "status")]
353 Status,
354}
355
356#[derive(StructOpt, Clone, Debug)]
357pub enum DeprecatedConfiguratorCmd {
358 #[structopt(name = "suggest-mac")]
359 SuggestAccessPointMacAddress {
360 #[structopt(required = true)]
361 mac: MacAddress,
362 },
363}
364
365#[derive(StructOpt, Clone, Debug)]
366pub enum Opt {
367 #[structopt(name = "client")]
368 Client(PolicyClientCmd),
369 #[structopt(name = "ap")]
370 AccessPoint(PolicyAccessPointCmd),
371 #[structopt(name = "deprecated")]
372 Deprecated(DeprecatedConfiguratorCmd),
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use test_case::test_case;
379
380 #[fuchsia::test]
382 fn test_construct_config_wep() {
383 test_construct_config_security(wlan_policy::SecurityType::Wep, SecurityTypeArg::Wep);
384 }
385
386 #[fuchsia::test]
388 fn test_construct_config_wpa() {
389 test_construct_config_security(wlan_policy::SecurityType::Wpa, SecurityTypeArg::Wpa);
390 }
391
392 #[fuchsia::test]
394 fn test_construct_config_wpa2() {
395 test_construct_config_security(wlan_policy::SecurityType::Wpa2, SecurityTypeArg::Wpa2);
396 }
397
398 #[fuchsia::test]
400 fn test_construct_config_wpa3() {
401 test_construct_config_security(wlan_policy::SecurityType::Wpa3, SecurityTypeArg::Wpa3);
402 }
403
404 #[fuchsia::test]
407 fn test_construct_config_open() {
408 let open_config = PolicyNetworkConfig {
409 ssid: "some_ssid".to_string(),
410 security_type: None,
411 credential_type: None,
412 credential: Some("".to_string()),
413 };
414 let expected_cfg = wlan_policy::NetworkConfig {
415 id: Some(wlan_policy::NetworkIdentifier {
416 ssid: "some_ssid".as_bytes().to_vec(),
417 type_: wlan_policy::SecurityType::None,
418 }),
419 credential: Some(wlan_policy::Credential::None(wlan_policy::Empty {})),
420 ..Default::default()
421 };
422 let result_cfg = wlan_policy::NetworkConfig::from(open_config);
423 assert_eq!(expected_cfg, result_cfg);
424 }
425
426 #[fuchsia::test]
429 fn test_construct_config_open_with_omitted_args() {
430 let open_config = PolicyNetworkConfig {
431 ssid: "some_ssid".to_string(),
432 security_type: Some(SecurityTypeArg::None),
433 credential_type: Some(CredentialTypeArg::None),
434 credential: Some("".to_string()),
435 };
436 let expected_cfg = wlan_policy::NetworkConfig {
437 id: Some(wlan_policy::NetworkIdentifier {
438 ssid: "some_ssid".as_bytes().to_vec(),
439 type_: wlan_policy::SecurityType::None,
440 }),
441 credential: Some(wlan_policy::Credential::None(wlan_policy::Empty {})),
442 ..Default::default()
443 };
444 let result_cfg = wlan_policy::NetworkConfig::from(open_config);
445 assert_eq!(expected_cfg, result_cfg);
446 }
447
448 #[fuchsia::test]
451 fn test_construct_config_password_provided_no_security() {
452 let password = "mypassword";
453 let ssid = "some_ssid";
454 let arg_config = PolicyNetworkConfig {
455 ssid: ssid.to_string(),
456 security_type: None,
457 credential_type: None,
458 credential: Some(password.to_string()),
459 };
460 let expected_cfg = wlan_policy::NetworkConfig {
461 id: Some(wlan_policy::NetworkIdentifier {
462 ssid: ssid.as_bytes().to_vec(),
463 type_: wlan_policy::SecurityType::Wpa2,
464 }),
465 credential: Some(wlan_policy::Credential::Password(password.as_bytes().to_vec())),
466 ..Default::default()
467 };
468 let result_cfg = wlan_policy::NetworkConfig::from(arg_config);
469 assert_eq!(expected_cfg, result_cfg);
470 }
471
472 #[fuchsia::test]
475 fn test_construct_config_psk_provided_no_security() {
476 let psk = "123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF1234".to_string();
477 let psk_bytes = hex::decode(psk.as_bytes().to_vec()).unwrap();
478 let ssid = "some_ssid";
479 let arg_config = PolicyNetworkConfig {
480 ssid: ssid.to_string(),
481 security_type: None,
482 credential_type: None,
483 credential: Some(psk),
484 };
485 let expected_cfg = wlan_policy::NetworkConfig {
486 id: Some(wlan_policy::NetworkIdentifier {
487 ssid: ssid.as_bytes().to_vec(),
488 type_: wlan_policy::SecurityType::Wpa2,
489 }),
490 credential: Some(wlan_policy::Credential::Psk(psk_bytes)),
491 ..Default::default()
492 };
493 let result_cfg = wlan_policy::NetworkConfig::from(arg_config);
494 assert_eq!(expected_cfg, result_cfg);
495 }
496
497 #[fuchsia::test]
500 fn test_construct_config_psk() {
501 const ASCII_ZERO: u8 = 49;
503 let psk =
504 String::from_utf8([ASCII_ZERO; 64].to_vec()).expect("Failed to create PSK test value");
505 let wpa_config = PolicyNetworkConfig {
506 ssid: "some_ssid".to_string(),
507 security_type: Some(SecurityTypeArg::Wpa2),
508 credential_type: Some(CredentialTypeArg::Psk),
509 credential: Some(psk),
510 };
511 let expected_cfg = wlan_policy::NetworkConfig {
512 id: Some(wlan_policy::NetworkIdentifier {
513 ssid: "some_ssid".as_bytes().to_vec(),
514 type_: wlan_policy::SecurityType::Wpa2,
515 }),
516 credential: Some(wlan_policy::Credential::Psk([17; 32].to_vec())),
517 ..Default::default()
518 };
519 let result_cfg = wlan_policy::NetworkConfig::from(wpa_config);
520 assert_eq!(expected_cfg, result_cfg);
521 }
522
523 fn test_construct_config_security(
526 fidl_type: wlan_policy::SecurityType,
527 tool_type: SecurityTypeArg,
528 ) {
529 let args_config = PolicyNetworkConfig {
530 ssid: "some_ssid".to_string(),
531 security_type: Some(tool_type),
532 credential_type: Some(CredentialTypeArg::Password),
533 credential: Some("some_password_here".to_string()),
534 };
535 let expected_cfg = wlan_policy::NetworkConfig {
536 id: Some(wlan_policy::NetworkIdentifier {
537 ssid: "some_ssid".as_bytes().to_vec(),
538 type_: fidl_type,
539 }),
540 credential: Some(wlan_policy::Credential::Password(
541 "some_password_here".as_bytes().to_vec(),
542 )),
543 ..Default::default()
544 };
545 let result_cfg = wlan_policy::NetworkConfig::from(args_config);
546 assert_eq!(expected_cfg, result_cfg);
547 }
548
549 #[test_case(Some(SecurityTypeArg::None))]
552 #[test_case(None)]
553 fn test_try_parse_credentialcredential_not_provided(security_type: Option<SecurityTypeArg>) {
554 let args = RemoveArgs {
555 ssid: "some_ssid".to_string(),
556 security_type,
557 credential_type: None,
558 credential: None,
559 };
560 let expected_credential = None;
561 let result = args.try_parse_credential().expect("error occurred parsing credential");
562 assert_eq!(result, expected_credential);
563 }
564
565 #[fuchsia::test]
568 fn test_try_parse_credential_open_network_credential_empty_string() {
569 let args = RemoveArgs {
570 ssid: "some_ssid".to_string(),
571 security_type: Some(SecurityTypeArg::None),
572 credential_type: None,
573 credential: Some("".to_string()),
574 };
575 let expected_credential = Some(wlan_policy::Credential::None(wlan_policy::Empty));
576 let result = args.try_parse_credential().expect("error occurred parsing credential");
577 assert_eq!(result, expected_credential);
578 }
579
580 #[fuchsia::test]
583 fn test_try_parse_credential_with_no_type() {
584 let password = "somepassword";
585 let args = RemoveArgs {
586 ssid: "some_ssid".to_string(),
587 security_type: None,
588 credential_type: None,
589 credential: Some(password.to_string()),
590 };
591 let expected_credential =
592 Some(wlan_policy::Credential::Password(password.as_bytes().to_vec()));
593 let result = args.try_parse_credential().expect("error occurred parsing credential");
594 assert_eq!(result, expected_credential);
595 assert_eq!(args.parse_security(), None);
596 }
597
598 #[fuchsia::test]
601 fn test_try_parse_credential_exact_args_provided() {
602 let psk = "123456789ABCDEF123456789ABCDEF123456789ABCDEF123456789ABCDEF1234";
603 let args = RemoveArgs {
604 ssid: "some_ssid".to_string(),
605 security_type: Some(SecurityTypeArg::None),
606 credential_type: Some(CredentialTypeArg::Psk),
607 credential: Some(psk.to_string()),
608 };
609 let expected_credential = Some(wlan_policy::Credential::Psk(hex::decode(psk).unwrap()));
610 let result = args.try_parse_credential().expect("error occurred parsing credential");
611 assert_eq!(result, expected_credential);
612 }
613
614 #[fuchsia::test]
617 fn test_try_parse_credential_password_with_no_value_is_err() {
618 let args = RemoveArgs {
619 ssid: "some_ssid".to_string(),
620 security_type: Some(SecurityTypeArg::Wpa2),
621 credential_type: Some(CredentialTypeArg::Password),
622 credential: None,
623 };
624 args.try_parse_credential().expect_err("an error should occur parsing this credential");
625 }
626}