1#![cfg(unix)]
47#![warn(missing_docs)]
48#![allow(clippy::style, clippy::should_implement_trait)]
49
50mod utils;
51
52const BUF_SIZE: usize = 512;
53const DEFAULT_ROOT: &str = "/var/run/wpa_supplicant/";
54
55#[cfg(not(unix))]
56compile_error!("Supports only unix targets");
57
58#[cfg(target_os = "android")]
59const LOCAL_SOCKET_DIR: &str = "/data/misc/wifi/sockets";
60#[cfg(not(target_os = "android"))]
61const LOCAL_SOCKET_DIR: &str = "/tmp";
62const LOCAL_SOCKET_PREFIX: &str = "wpa_ctrl_";
63const UNSOLICITED_PREFIX: char = '<';
64type LocalSocketName = str_buf::StrBuf<23>;
65type BssidStr = str_buf::StrBuf<17>;
66type SsidStr = str_buf::StrBuf<32>;
67
68use std::os::unix::net::UnixDatagram;
69use std::{fs, io, path, net};
70use core::{str, time};
71use core::sync::atomic::{AtomicU32, Ordering};
72use core::fmt::{self, Write};
73
74pub trait SuffixGenerator {
80 fn generate_suffix(&self) -> u32;
82}
83
84impl SuffixGenerator for () {
85 fn generate_suffix(&self) -> u32 {
86 static COUNTER: AtomicU32 = AtomicU32::new(1);
87 COUNTER.fetch_add(1, Ordering::SeqCst)
88 }
89}
90
91pub struct Options<S: SuffixGenerator> {
93 pub suffix: S
97}
98
99static DEFAULT_OPTIONS: Options<()> = Options {
100 suffix: ()
101};
102
103fn local_socket_name(generator: &impl SuffixGenerator) -> LocalSocketName {
104 let mut name = LocalSocketName::new();
105 let _ = write!(&mut name, "{}{}", LOCAL_SOCKET_PREFIX, generator.generate_suffix());
106 name
107}
108
109pub struct QuotedValue<T: fmt::Display>(pub T);
111
112impl<T: fmt::Display> fmt::Display for QuotedValue<T> {
113 #[inline]
114 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
115 fmt.write_fmt(format_args!("\"{}\"", self.0))
116 }
117}
118
119#[derive(Copy, Clone, Debug)]
121pub struct WpaControllerBuilder<'a> {
122 pub root: &'a str,
124 pub read_timeout: Option<time::Duration>,
126}
127
128impl WpaControllerBuilder<'static> {
129 pub const fn new() -> Self {
134 Self {
135 root: DEFAULT_ROOT,
136 read_timeout: Some(time::Duration::from_secs(10)),
137 }
138 }
139}
140
141impl<'a> WpaControllerBuilder<'a> {
142 #[inline]
143 #[allow(clippy::needless_lifetimes)]
144 pub const fn set_root<'b>(self, new: &'b str) -> WpaControllerBuilder<'b> {
146 WpaControllerBuilder {
147 root: new,
148 read_timeout: self.read_timeout,
149 }
150 }
151
152 #[inline]
153 pub const fn set_read_timeout(mut self, read_timeout: Option<time::Duration>) -> Self {
158 self.read_timeout = read_timeout;
159 self
160 }
161
162 #[inline]
163 pub fn open(self, interface: &str) -> Result<WpaController, io::Error> {
165 let path = path::Path::new(self.root).join(interface);
166 WpaController::open_path(&path)
167 }
168
169 #[inline]
170 pub fn open_with(self, interface: &str, options: &Options<impl SuffixGenerator>) -> Result<WpaController, io::Error> {
172 let path = path::Path::new(self.root).join(interface);
173 WpaController::open_path_with(&path, options)
174 }
175}
176
177pub struct WpaControlReq {
181 buf: str_buf::StrBuf<127>,
182}
183
184impl WpaControlReq {
185 #[inline]
186 pub const fn raw(text: &str) -> Self {
190 Self {
191 buf: str_buf::StrBuf::from_str(text)
192 }
193 }
194
195 #[inline]
196 pub const fn ping() -> Self {
198 Self::raw("PING")
199 }
200
201 #[inline]
202 pub const fn status() -> Self {
204 Self::raw("STATUS")
205 }
206
207 #[inline]
208 pub const fn scan() -> Self {
210 Self::raw("SCAN")
211 }
212
213 #[inline]
214 pub const fn scan_results() -> Self {
216 Self::raw("SCAN_RESULTS")
217 }
218
219 #[inline]
220 pub const fn disconnect() -> Self {
222 Self::raw("DISCONNECT")
223 }
224
225 #[inline]
226 pub const fn reassociate() -> Self {
228 Self::raw("REASSOCIATE")
229 }
230
231 #[inline]
232 pub const fn list_networks() -> Self {
234 Self::raw("LIST_NETWORKS")
235 }
236
237 #[inline]
238 pub fn add_network() -> Self {
240 Self::raw("ADD_NETWORK")
241 }
242
243 #[inline]
244 pub fn get_network(id: Id, var: &str) -> Self {
246 let mut this = Self::raw("SET_NETWORK");
247 let _ = write!(&mut this.buf, " {}", id.0);
248 this.buf.push_str(" ");
249 this.buf.push_str(var);
250 this
251 }
252
253 #[inline]
254 pub fn set_network(id: Id, var: &str, value: impl fmt::Display) -> Self {
256 let mut this = Self::raw("SET_NETWORK");
257 let _ = write!(&mut this.buf, " {}", id.0);
258 this.buf.push_str(" ");
259 this.buf.push_str(var);
260 let _ = write!(&mut this.buf, " {}", value);
261 this
262 }
263
264 #[inline]
265 pub fn select_network(id: Id) -> Self {
267 let mut this = Self::raw("SELECT_NETWORK");
268 let _ = write!(&mut this.buf, " {}", id.0);
269 this
270 }
271
272 #[inline]
273 pub fn enable_network(id: Id) -> Self {
275 let mut this = Self::raw("ENABLE_NETWORK");
276 let _ = write!(&mut this.buf, " {}", id.0);
277 this
278 }
279
280 #[inline]
281 pub fn disable_network(id: Id) -> Self {
283 let mut this = Self::raw("DISABLE_NETWORK");
284 let _ = write!(&mut this.buf, " {}", id.0);
285 this
286 }
287
288 #[inline]
289 pub fn remove_network(id: Id) -> Self {
291 let mut this = Self::raw("REMOVE_NETWORK");
292 let _ = write!(&mut this.buf, " {}", id.0);
293 this
294 }
295
296 #[inline]
297 pub fn remove_network_all() -> Self {
299 Self::raw("REMOVE_NETWORK all")
300 }
301
302
303 #[inline]
304 pub fn save_config() -> Self {
306 Self::raw("SAVE_CONFIG")
307 }
308}
309
310impl fmt::Debug for WpaControlReq {
311 #[inline(always)]
312 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
313 fmt::Debug::fmt(&self.buf, fmt)
314 }
315}
316
317
318#[derive(Copy, Clone, Debug)]
319pub struct Success;
321
322#[derive(Copy, Clone, Debug)]
323pub struct Fail;
325
326#[derive(Copy, Clone, Debug)]
327pub struct Pong;
329
330#[derive(Copy, Clone, Debug)]
331#[repr(transparent)]
332#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
333#[cfg_attr(feature = "serde", serde(transparent))]
334pub struct Id(pub u32);
336
337#[derive(Copy, Clone, Debug)]
338pub enum WpaState {
340 Unknown,
342 Disconnected,
347 InterfaceDisabled,
353 Inactive,
359 Scanning,
363 Authenticating,
369 Associating,
376 Associated,
382 Handshake,
388 GroupHandshake,
392 Completed
394}
395
396impl WpaState {
397 pub fn from_str(text: &str) -> Self {
399 if text.eq_ignore_ascii_case("COMPLETED") {
400 Self::Completed
401 } else if text.eq_ignore_ascii_case("GROUP_HANDSHAKE") {
402 Self::GroupHandshake
403 } else if text.eq_ignore_ascii_case("4WAY_HANDSHAKE") {
404 Self::Handshake
405 } else if text.eq_ignore_ascii_case("ASSOCIATED") {
406 Self::Associated
407 } else if text.eq_ignore_ascii_case("ASSOCIATING") {
408 Self::Associating
409 } else if text.eq_ignore_ascii_case("AUTHENTICATING") {
410 Self::Authenticating
411 } else if text.eq_ignore_ascii_case("SCANNING") {
412 Self::Scanning
413 } else if text.eq_ignore_ascii_case("INACTIVE") {
414 Self::Inactive
415 } else if text.eq_ignore_ascii_case("INTERFACE_DISABLED") {
416 Self::InterfaceDisabled
417 } else if text.eq_ignore_ascii_case("DISCONNECTED") {
418 Self::Disconnected
419 } else {
420 Self::Unknown
421 }
422 }
423}
424
425#[derive(Clone, Debug)]
426pub struct WpaStatus {
428 pub state: WpaState,
430 pub ip: Option<net::IpAddr>,
432 pub ssid: Option<String>
434}
435
436impl WpaStatus {
437 pub fn from_str(text: &str) -> Option<Self> {
441 let mut state = None;
442 let mut ip = None;
443 let mut ssid = None;
444
445 for line in text.lines() {
446 if line.is_empty() {
447 continue;
448 }
449
450 let mut split = line.splitn(2, '=');
451 let var = split.next().unwrap();
452 if let Some(value) = split.next() {
453 if var.eq_ignore_ascii_case("wpa_state") {
454 state = Some(WpaState::from_str(value));
455 } else if var.eq_ignore_ascii_case("ip_address") {
456 ip = value.parse().ok();
457 } else if var.eq_ignore_ascii_case("ssid") {
458 ssid = Some(value.to_owned());
459 } else {
460 continue;
462 }
463 } else {
464 return None;
466 }
467 }
468
469 state.map(|state| Self {
470 state,
471 ip,
472 ssid,
473 })
474 }
475}
476
477
478#[derive(Clone, Copy, Debug, Default)]
480#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
481pub struct WpaNetworkFlags {
482 pub current: bool,
484 pub disabled: bool,
486 pub p2p_persistent: bool
488}
489
490impl WpaNetworkFlags {
491 #[inline(always)]
492 pub fn from_str(mut text: &str) -> Self {
494 let mut result = WpaNetworkFlags {
495 current: false,
496 disabled: false,
497 p2p_persistent: false,
498 };
499
500 if !text.is_empty() {
501 while let Some(start_flag) = text.strip_prefix('[') {
502 if let Some(end) = start_flag.find(']') {
503 let flag = &start_flag[..end];
504 if flag.eq_ignore_ascii_case("CURRENT") {
505 result.current = true;
506 } else if flag.eq_ignore_ascii_case("DISABLED") {
507 result.disabled = true;
508 } else if flag.eq_ignore_ascii_case("P2P-PERSISTENT") {
509 result.p2p_persistent = true;
510 }
511 text = &start_flag[end..];
512 } else {
513 break;
514 }
515 }
516 }
517
518 result
519 }
520}
521
522#[derive(Clone, Debug)]
524#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
525pub struct WpaNetwork {
526 pub id: Id,
528 pub ssid: String,
530 pub bssid: String,
532 pub flags: WpaNetworkFlags,
534}
535
536pub struct WpaNetworkList<'a> {
538 lines: str::Lines<'a>,
539}
540
541impl<'a> Iterator for WpaNetworkList<'a> {
542 type Item = WpaNetwork;
543
544 #[inline]
545 fn next(&mut self) -> Option<Self::Item> {
546 let line = self.lines.next()?;
547 let mut parts = line.splitn(4, '\t');
548 let network = WpaNetwork {
549 id: Id(parts.next().unwrap().parse().ok()?),
550 ssid: parts.next()?.to_owned(),
551 bssid: parts.next()?.to_owned(),
552 flags: WpaNetworkFlags::from_str(parts.next().unwrap_or("")),
553 };
554 Some(network)
555 }
556}
557
558#[derive(Clone, Debug, PartialEq, Eq)]
559#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
560pub enum WpaAuth {
562 Wpa2Eap,
564 WpaEap,
566 Wpa2Psk,
568 WpaPsk,
570 Open
572}
573
574impl WpaAuth {
575 pub fn as_str(&self) -> &'static str {
577 match self {
578 Self::Wpa2Eap => "WPA2-EAP",
579 Self::WpaEap => "WPA-EAP",
580 Self::Wpa2Psk => "WPA2-PSK",
581 Self::WpaPsk => "WPA-PSK",
582 Self::Open => "OPEN"
583 }
584 }
585}
586
587#[derive(Clone, Debug, PartialEq, Eq)]
588#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
589pub enum WpaEncryption {
591 NONE,
593 WEP,
597 TKIP,
601 CCMP
603}
604
605impl WpaEncryption {
606 pub fn as_str(&self) -> &'static str {
608 match self {
609 Self::WEP => "WEP",
610 Self::TKIP => "TKIP",
611 Self::CCMP => "CCMP",
612 Self::NONE => "",
613 }
614 }
615
616 #[inline]
617 pub fn is_none(&self) -> bool {
619 matches!(self, Self::NONE)
620 }
621}
622
623#[derive(Clone, Debug, PartialEq, Eq)]
624#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
625pub struct WpaScanResultFlags {
627 pub auth: WpaAuth,
629 pub encryption: WpaEncryption,
631}
632
633impl WpaScanResultFlags {
634 pub fn from_str(text: &str) -> Self {
636 WpaScanResultFlags {
637 auth: if text.starts_with("[WPA2-EAP") {
638 WpaAuth::Wpa2Eap
639 } else if text.starts_with("[WPA-EAP") {
640 WpaAuth::WpaEap
641 } else if text.starts_with("[WPA2-PSK") {
642 WpaAuth::Wpa2Psk
643 } else if text.starts_with("[WPA-PSK") {
644 WpaAuth::WpaPsk
645 } else {
646 WpaAuth::Open
647 },
648 encryption: if text.contains("-CCMP") {
649 WpaEncryption::CCMP
650 } else if text.contains("-TKIP") {
651 WpaEncryption::TKIP
652 } else if text.contains("WEP") {
653 WpaEncryption::WEP
654 } else {
655 WpaEncryption::NONE
656 }
657 }
658 }
659}
660
661impl fmt::Display for WpaScanResultFlags {
662 #[inline]
663 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
664 fmt.write_str(self.auth.as_str())?;
665 if !self.encryption.is_none() {
666 fmt.write_str("-")?;
667 fmt.write_str(self.encryption.as_str())?
668 }
669 Ok(())
670 }
671}
672
673#[derive(Clone, Debug)]
675#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
676pub struct WpaScanResult {
677 pub bssid: BssidStr,
679 pub freq: u32,
681 pub level: i16,
683 pub flags: WpaScanResultFlags,
685 pub ssid: SsidStr,
687}
688
689impl WpaScanResult {
690 pub fn singal_level_percent(&self) -> u8 {
694 if self.level <= -100 {
695 0u8
696 } else if self.level >= -50 {
697 100u8
698 } else {
699 2i16.saturating_mul(self.level.saturating_add(100)) as u8
700 }
701 }
702}
703
704pub struct WpaScanResults<'a> {
706 lines: str::Lines<'a>,
707}
708
709impl<'a> Iterator for WpaScanResults<'a> {
710 type Item = WpaScanResult;
711
712 #[inline]
713 fn next(&mut self) -> Option<Self::Item> {
714 let line = self.lines.next()?;
715 let mut parts = line.splitn(5, '\t');
716
717 let mut bssid = BssidStr::new();
718 bssid.push_str(parts.next()?);
719
720 let result = WpaScanResult {
721 bssid,
722 freq: parts.next()?.parse().ok()?,
723 level: parts.next()?.parse().unwrap_or(-100),
725 flags: WpaScanResultFlags::from_str(parts.next().unwrap_or("")),
726 ssid: {
727 let mut ssid = SsidStr::new();
728 ssid.push_str(parts.next()?);
729 ssid
730 }
731 };
732
733 Some(result)
734 }
735}
736
737pub struct WpaControlMessage<'a> {
739 pub raw: &'a str,
741}
742
743impl<'a> WpaControlMessage<'a> {
744 #[inline(always)]
745 pub const fn is_unsolicited(&self) -> bool {
747 !self.raw.is_empty() && self.raw.as_bytes()[0] == UNSOLICITED_PREFIX as u8
748 }
749
750 pub fn as_pong(&self) -> Option<Pong> {
752 if self.raw.eq_ignore_ascii_case("pong") {
753 Some(Pong)
754 } else {
755 None
756 }
757 }
758
759 pub fn as_success(&self) -> Option<Success> {
761 if self.raw.eq_ignore_ascii_case("ok") {
762 Some(Success)
763 } else {
764 None
765 }
766 }
767
768 pub fn as_fail(&self) -> Option<Fail> {
770 if self.raw.eq_ignore_ascii_case("fail") {
771 Some(Fail)
772 } else {
773 None
774 }
775 }
776
777 pub fn as_status(&self) -> Option<WpaStatus> {
779 WpaStatus::from_str(self.raw)
780 }
781
782 pub fn as_network_id(&self) -> Option<Id> {
784 self.raw.parse().map(|id| Id(id)).ok()
785 }
786
787 pub fn as_network_list(&self) -> Option<WpaNetworkList<'_>> {
789 let mut lines = self.raw.lines();
790 let line = lines.next().unwrap();
791 let header = utils::split::<4>(line, '/')?;
792 if header[0] == "network id" && header[1] == "ssid" && header[2] == "bssid" && header[3] == "flags" {
793 Some(WpaNetworkList {
794 lines
795 })
796 } else {
797 None
798 }
799 }
800
801 pub fn as_scan_results(&self) -> Option<WpaScanResults<'_>> {
803 let mut lines = self.raw.lines();
804 let line = lines.next().unwrap();
805 let header = utils::split::<5>(line, '/')?;
806 if header[0] == "bssid" && header[1] == "frequency" && header[2] == "signal level" && header[3] == "flags" && header[4] == "ssid" {
808 Some(WpaScanResults {
809 lines
810 })
811 } else {
812 None
813 }
814
815 }
816}
817
818impl<'a> fmt::Debug for WpaControlMessage<'a> {
819 #[inline(always)]
820 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
821 fmt::Debug::fmt(self.raw, fmt)
822 }
823}
824
825pub struct WpaController {
831 socket: UnixDatagram,
832 local: path::PathBuf,
833}
834
835impl WpaController {
836 #[inline(always)]
837 pub fn open<P: AsRef<path::Path>>(path: P) -> Result<Self, io::Error> {
839 Self::open_path(path.as_ref())
840 }
841
842 #[inline(always)]
843 pub fn open_path(path: &path::Path) -> Result<Self, io::Error> {
845 Self::open_path_with(path, &DEFAULT_OPTIONS)
846 }
847
848 pub fn open_path_with(path: &path::Path, options: &Options<impl SuffixGenerator>) -> Result<Self, io::Error> {
850 let local_name = local_socket_name(&options.suffix);
851 let local = path::Path::new(LOCAL_SOCKET_DIR).join(local_name.as_str());
852
853 let _ = fs::remove_file(&local);
855
856 let socket = UnixDatagram::bind(&local)?;
857 let this = Self {
858 socket,
859 local,
860 };
861 this.socket.connect(path)?;
862 Ok(this)
863 }
864
865 pub fn into_buffered(self) -> BufferedWpaController {
867 BufferedWpaController {
868 inner: self,
869 buffer: [0; BUF_SIZE],
870 }
871 }
872
873 #[inline]
874 pub fn request(&self, req: WpaControlReq) -> Result<usize, io::Error> {
876 self.socket.send(req.buf.as_bytes())
877 }
878
879 pub fn recv<'a>(&self, buffer: &'a mut [u8]) -> Result<Option<WpaControlMessage<'a>>, io::Error> {
881 loop {
882 match self.socket.recv(buffer) {
883 Ok(len) => {
884 let msg = match core::str::from_utf8(&buffer[..len]) {
885 Ok(msg) => msg.trim(),
886 Err(error) => break Err(io::Error::new(io::ErrorKind::InvalidData, error))
887 };
888
889 break Ok(Some(WpaControlMessage {
890 raw: msg,
891 }))
892 },
893 Err(error) => match error.kind() {
894 io::ErrorKind::Interrupted => continue,
895 io::ErrorKind::TimedOut => break Ok(None),
896 _ => break Err(error),
897 }
898 }
899 }
900 }
901
902 pub fn recv_req_result(&self, buffer: &mut [u8]) -> Option<Result<Result<(), ()>, io::Error>> {
914 loop {
915 match self.recv(buffer) {
916 Ok(Some(msg)) => {
917 if msg.as_success().is_some() {
918 break Some(Ok(Ok(())));
919 } else if msg.as_fail().is_some() {
920 break Some(Ok(Err(())));
921 } else {
922 continue
923 }
924 },
925 Ok(None) => break None,
926 Err(error) => return Some(Err(error)),
927 }
928 }
929 }
930
931 pub fn add_network(&self, ssid: &str, wpa_pass: Option<&str>, hidden: bool, buffer: &mut [u8]) -> Result<Id, io::Error> {
943 self.request(WpaControlReq::add_network())?;
944 let id = loop {
945 match self.recv(buffer)? {
946 Some(msg) => match msg.as_network_id() {
947 Some(id) => break id,
948 None => continue,
949 },
950 None => return Err(io::Error::new(io::ErrorKind::TimedOut, "no response to add_network")),
951 }
952 };
953
954 self.request(WpaControlReq::set_network(id, "ssid", QuotedValue(ssid)))?;
955 match self.recv_req_result(buffer) {
956 Some(Ok(Ok(()))) => (),
957 Some(Ok(Err(()))) => return Err(io::Error::new(io::ErrorKind::Other, format!("set_network id={} ssid {} failed", id.0, QuotedValue(ssid)))),
958 Some(Err(error)) => return Err(error),
959 None => return Err(io::Error::new(io::ErrorKind::Other, format!("set_network id={} ssid {} had no ok/fail reply", id.0, QuotedValue(ssid)))),
960 }
961 match wpa_pass {
962 Some(wpa_pass) => {
963 let wpa_pass = QuotedValue(wpa_pass);
964 self.request(WpaControlReq::set_network(id, "psk", &wpa_pass))?;
965 match self.recv_req_result(buffer) {
966 Some(Ok(Ok(()))) => (),
967 Some(Ok(Err(()))) => return Err(io::Error::new(io::ErrorKind::Other, format!("set_network id={} psk {} failed", id.0, wpa_pass))),
968 Some(Err(error)) => return Err(error),
969 None => return Err(io::Error::new(io::ErrorKind::Other, format!("set_network id={} psk {} had no ok/fail reply", id.0, wpa_pass))),
970 }
971 },
972 None => {
973 self.request(WpaControlReq::set_network(id, "key_mgmt", "NONE"))?;
974 match self.recv_req_result(buffer) {
975 Some(Ok(Ok(()))) => (),
976 Some(Ok(Err(()))) => return Err(io::Error::new(io::ErrorKind::Other, format!("set_network id={} key_mgmt NONE failed", id.0))),
977 Some(Err(error)) => return Err(error),
978 None => return Err(io::Error::new(io::ErrorKind::Other, format!("set_network id={} key_mgmt NONE had no ok/fail reply", id.0))),
979 }
980 },
981 }
982
983 if hidden {
984 self.request(WpaControlReq::set_network(id, "scan_ssid", 1))?;
985 match self.recv_req_result(buffer) {
986 Some(Ok(Ok(()))) => (),
987 Some(Ok(Err(()))) => return Err(io::Error::new(io::ErrorKind::Other, format!("set_network id={} scan_ssid 1 failed", id.0))),
988 Some(Err(error)) => return Err(error),
989 None => return Err(io::Error::new(io::ErrorKind::Other, format!("set_network id={} scan_ssid 1 had no ok/fail reply", id.0))),
990 }
991 }
992
993 Ok(id)
994 }
995
996 pub fn remove_network(&self, id: Id, buffer: &mut [u8]) -> Result<(), io::Error> {
998 self.request(WpaControlReq::remove_network(id))?;
999 match self.recv_req_result(buffer) {
1000 Some(Ok(Ok(()))) => Ok(()),
1001 Some(Ok(Err(()))) => return Err(io::Error::new(io::ErrorKind::Other, format!("remove_network id={}", id.0))),
1002 Some(Err(error)) => return Err(error),
1003 None => return Err(io::Error::new(io::ErrorKind::Other, format!("remove_network id={} has no reply", id.0))),
1004 }
1005 }
1006
1007 pub fn select_network(&self, id: Id, buffer: &mut [u8]) -> Result<(), io::Error> {
1009 self.request(WpaControlReq::select_network(id))?;
1010 match self.recv_req_result(buffer) {
1011 Some(Ok(Ok(()))) => Ok(()),
1012 Some(Ok(Err(()))) => return Err(io::Error::new(io::ErrorKind::Other, format!("select_network id={}", id.0))),
1013 Some(Err(error)) => return Err(error),
1014 None => return Err(io::Error::new(io::ErrorKind::Other, format!("select_network id={} has no reply", id.0))),
1015 }
1016 }
1017
1018 pub fn reconfigure(&self, buffer: &mut [u8]) -> Result<(), io::Error> {
1020 self.request(WpaControlReq::raw("RECONFIGURE"))?;
1021 match self.recv_req_result(buffer) {
1022 Some(Ok(Ok(()))) => Ok(()),
1023 Some(Ok(Err(r))) => return Err(io::Error::new(io::ErrorKind::Other, format!("reconfigure ret={:?}", r))),
1024 Some(Err(error)) => return Err(error),
1025 None => return Err(io::Error::new(io::ErrorKind::Other, "reconfigure has no reply".to_owned())),
1026 }
1027 }
1028}
1029
1030impl Drop for WpaController {
1031 #[inline]
1032 fn drop(&mut self) {
1033 let _ = self.socket.shutdown(net::Shutdown::Both);
1034 let _ = fs::remove_file(&self.local);
1035 }
1036}
1037
1038pub struct BufferedWpaController {
1040 buffer: [u8; BUF_SIZE],
1041 inner: WpaController,
1042}
1043
1044impl BufferedWpaController {
1045 #[inline]
1046 pub fn request(&self, req: WpaControlReq) -> Result<usize, io::Error> {
1048 self.inner.socket.send(req.buf.as_bytes())
1049 }
1050
1051 #[inline]
1052 pub fn recv(&mut self) -> Result<Option<WpaControlMessage<'_>>, io::Error> {
1054 self.inner.recv(&mut self.buffer)
1055 }
1056
1057 #[inline]
1058 pub fn recv_req_result(&mut self) -> Option<Result<Result<(), ()>, io::Error>> {
1070 self.inner.recv_req_result(&mut self.buffer)
1071 }
1072
1073 #[inline]
1074 pub fn add_network(&mut self, ssid: &str, wpa_pass: Option<&str>, hidden: bool) -> Result<Id, io::Error> {
1086 self.inner.add_network(ssid, wpa_pass, hidden, &mut self.buffer)
1087 }
1088
1089 #[inline]
1090 pub fn remove_network(&mut self, id: Id) -> Result<(), io::Error> {
1092 self.inner.remove_network(id, &mut self.buffer)
1093 }
1094
1095 #[inline]
1096 pub fn select_network(&mut self, id: Id) -> Result<(), io::Error> {
1098 self.inner.select_network(id, &mut self.buffer)
1099 }
1100
1101 #[inline]
1102 pub fn reconfigure(&mut self) -> Result<(), io::Error> {
1104 self.inner.reconfigure(&mut self.buffer)
1105 }
1106}