wpa_ctrl/
lib.rs

1//!WPA controller
2//!
3//!## Usage
4//!
5//!```rust,no_run
6//!use wpa_ctrl::{WpaControlReq, WpaControllerBuilder};
7//!
8//!const WPA_CTRL_BUILD: WpaControllerBuilder<'static> = WpaControllerBuilder::new();
9//!
10//!let mut ctrl = match WPA_CTRL_BUILD.open("wlan0") {
11//!    Ok(ctrl) => ctrl.into_buffered(),
12//!    Err(error) => panic!("Cannot open wlan0"),
13//!};
14//!
15//!ctrl.request(WpaControlReq::status()).expect("Successful command");
16//!while let Some(resp) = ctrl.recv().expect("To read message") {
17//!    //Skip messages that are not intended as responses
18//!    if resp.is_unsolicited() {
19//!        continue;
20//!    }
21//!
22//!    if let Some(status) = resp.as_status() {
23//!        println!("Network status={:?}", status);
24//!        break;
25//!    }
26//!}
27//!```
28//!
29//!## Usage scenarios
30//!
31//!### Add new network
32//!
33//!- Optionally `scan` and check list of networks using `scan_results`
34//!- `add_network` which returns returns `id` of network
35//!- `set_network <id> ssid "network name"` which specifies network's name to associate with
36//!- `set_network <id> psk "WAP password"` which specifies WPA password, only usable when network requires WPA security
37//!- `set_network <id> key_mgmt NONE` which specifies no security, required to connect to networks without password
38//!- `select_network <id>` - Select network for use.
39//!- `save_config` - Optionally to save configuration.
40//!
41//!### Reconnect
42//!
43//!- Optionally `disconnect`;
44//!- Run `reassociate` to start process of connecting to currently selected network
45
46#![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
74///Suffix Generator for socket name
75///
76///When wpa-control attempts to connect it shall bind unix socket with name `wpa_crtl_<counter>`
77///
78///Default implementation is implemented on ()
79pub trait SuffixGenerator {
80    ///Creates suffix for file name
81    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
91///Options to control behavior when creating instance
92pub struct Options<S: SuffixGenerator> {
93    ///Method to generate socket name to bind to.
94    ///
95    ///When not used, it uses static counter
96    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
109///Surrounds value with quotes, useful when setting `ssid` or `psk`
110pub 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///Client builder
120#[derive(Copy, Clone, Debug)]
121pub struct WpaControllerBuilder<'a> {
122    ///Folder where to look up wpa_supplicant's interfaces.
123    pub root: &'a str,
124    ///Read timeout for responses.
125    pub read_timeout: Option<time::Duration>,
126}
127
128impl WpaControllerBuilder<'static> {
129    ///Creates default instance.
130    ///
131    ///- `root` - `/var/run/wpa_supplicant/`
132    ///- `read_timeout` - `Some(Duration::from_secs(10))`
133    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    ///Changes root folder
145    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    ///Changes read_timeout
154    ///
155    ///If None, then block indefinitely, otherwise return error on timeout.
156    ///This library handles timeout error, returning `None` response
157    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    ///Attempts to open socket.
164    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    ///Attempts to open socket with options.
171    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
177///Request type
178///
179///Max message size 127 bytes
180pub struct WpaControlReq {
181    buf: str_buf::StrBuf<127>,
182}
183
184impl WpaControlReq {
185    #[inline]
186    ///Creates raw request, 127 bytes maximum
187    ///
188    ///Panics on overflow.
189    pub const fn raw(text: &str) -> Self {
190        Self {
191            buf: str_buf::StrBuf::from_str(text)
192        }
193    }
194
195    #[inline]
196    ///Creates PING request
197    pub const fn ping() -> Self {
198        Self::raw("PING")
199    }
200
201    #[inline]
202    ///Creates STATUS request
203    pub const fn status() -> Self {
204        Self::raw("STATUS")
205    }
206
207    #[inline]
208    ///Creates SCAN request
209    pub const fn scan() -> Self {
210        Self::raw("SCAN")
211    }
212
213    #[inline]
214    ///Creates SCAN_RESULTS request
215    pub const fn scan_results() -> Self {
216        Self::raw("SCAN_RESULTS")
217    }
218
219    #[inline]
220    ///Creates DISCONNECT request
221    pub const fn disconnect() -> Self {
222        Self::raw("DISCONNECT")
223    }
224
225    #[inline]
226    ///Creates REASSOCIATE request
227    pub const fn reassociate() -> Self {
228        Self::raw("REASSOCIATE")
229    }
230
231    #[inline]
232    ///Creates LIST_NETWORKS request
233    pub const fn list_networks() -> Self {
234        Self::raw("LIST_NETWORKS")
235    }
236
237    #[inline]
238    ///Creates ENABLE_NETWORK request
239    pub fn add_network() -> Self {
240        Self::raw("ADD_NETWORK")
241    }
242
243    #[inline]
244    ///Creates GET_NETWORK request
245    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    ///Creates SET_NETWORK request
255    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    ///Creates SELECT_NETWORK request
266    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    ///Creates ENABLE_NETWORK request
274    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    ///Creates DISABLE_NETWORK request
282    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    ///Creates REMOVE_NETWORK request
290    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    ///Creates REMOVE_NETWORK all request
298    pub fn remove_network_all() -> Self {
299        Self::raw("REMOVE_NETWORK all")
300    }
301
302
303    #[inline]
304    ///Creates SAVE_CONFIG request
305    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)]
319///Indicates success of command
320pub struct Success;
321
322#[derive(Copy, Clone, Debug)]
323///Indicates failure of command
324pub struct Fail;
325
326#[derive(Copy, Clone, Debug)]
327///Pong Message
328pub 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))]
334///Network id
335pub struct Id(pub u32);
336
337#[derive(Copy, Clone, Debug)]
338///Interface state
339pub enum WpaState {
340    ///Not recognized state
341    Unknown,
342    ///Disconnected state.
343    ///
344    ///This state indicates that client is not associated, but is likely to start looking for an
345    ///access point. This state is entered when a connection is lost.
346    Disconnected,
347    ///Interface disabled.
348    ///
349    ///This state is entered if the network interface is disabled, e.g., due to rfkill.
350    ///wpa_supplicant refuses any new operations that would use the radio until the interface has
351    ///been enabled.
352    InterfaceDisabled,
353    ///Inactive state (wpa_supplicant disabled)
354    ///
355    ///This state is entered if there are no enabled networks in the configuration. wpa_supplicant
356    ///is not trying to associate with a new network and external interaction (e.g., ctrl_iface
357    ///call to add or enable a network) is needed to start association.
358    Inactive,
359    ///Scanning for a network.
360    ///
361    ///This state is entered when wpa_supplicant starts scanning for a network.
362    Scanning,
363    ///Trying to authenticate with a BSS/SSID.
364    ///
365    ///This state is entered when wpa_supplicant has found a suitable BSS to authenticate with and
366    ///the driver is configured to try to authenticate with this BSS. This state is used only with
367    ///drivers that use wpa_supplicant as the SME.
368    Authenticating,
369    ///Trying to associate with a BSS/SSID.
370    ///
371    ///This state is entered when wpa_supplicant has found a suitable BSS to associate with and the
372    ///driver is configured to try to associate with this BSS in ap_scan=1 mode. When using
373    ///ap_scan=2 mode, this state is entered when the driver is configured to try to associate with
374    ///a network using the configured SSID and security policy.
375    Associating,
376    ///Association completed.
377    ///
378    ///This state is entered when the driver reports that association has been successfully
379    ///completed with an AP. If IEEE 802.1X is used (with or without WPA/WPA2), wpa_supplicant
380    ///remains in this state until the IEEE 802.1X/EAPOL authentication has been completed.
381    Associated,
382    ///WPA 4-Way Key Handshake in progress.
383    ///
384    ///This state is entered when WPA/WPA2 4-Way Handshake is started. In case of WPA-PSK, this
385    ///happens when receiving the first EAPOL-Key frame after association. In case of WPA-EAP, this
386    ///state is entered when the IEEE 802.1X/EAPOL authentication has been completed.
387    Handshake,
388    ///This state is entered when 4-Way Key Handshake has been completed (i.e., when the supplicant
389    ///sends out message 4/4) and when Group Key rekeying is started by the AP (i.e., when
390    ///supplicant receives message 1/2).
391    GroupHandshake,
392    ///Connected and authenticated.
393    Completed
394}
395
396impl WpaState {
397    ///Parses status from its textual representation
398    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)]
426///Interface status
427pub struct WpaStatus {
428    ///Interface state
429    pub state: WpaState,
430    ///Interface IP address, available if connected
431    pub ip: Option<net::IpAddr>,
432    ///SSID used by interface on successful connection.
433    pub ssid: Option<String>
434}
435
436impl WpaStatus {
437    ///Attempts to parse WPA Status from string.
438    ///
439    ///Returning `None` if it is invalid format (not lines in format `<var>=<value>`) or missing `wpa_status`
440    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                    //Not interested to us, so skip
461                    continue;
462                }
463            } else {
464                //STATUS output is always <var>=<value>
465                return None;
466            }
467        }
468
469        state.map(|state| Self {
470            state,
471            ip,
472            ssid,
473        })
474    }
475}
476
477
478///Network's flag, describing its current state.
479#[derive(Clone, Copy, Debug, Default)]
480#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
481pub struct WpaNetworkFlags {
482    ///Indicates that network is currently set to be used.
483    pub current: bool,
484    ///Indicates that network is disabled.
485    pub disabled: bool,
486    ///Network is part of p2p persistent group. Google what it means.
487    pub p2p_persistent: bool
488}
489
490impl WpaNetworkFlags {
491    #[inline(always)]
492    ///Parses network flags from flags string
493    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///Network description
523#[derive(Clone, Debug)]
524#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
525pub struct WpaNetwork {
526    ///Network id
527    pub id: Id,
528    ///Network's SSID. Can be empty string, when not set.
529    pub ssid: String,
530    ///Network's BSSID. Can be empty string, when not set.
531    pub bssid: String,
532    ///Network's flag
533    pub flags: WpaNetworkFlags,
534}
535
536///Iterator over list of networks
537pub 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))]
560///Describes network's auth method
561pub enum WpaAuth {
562    ///WPA2-EAP
563    Wpa2Eap,
564    ///WPA-EAP
565    WpaEap,
566    ///WPA2-PSK
567    Wpa2Psk,
568    ///WPA-PSK
569    WpaPsk,
570    ///Open network
571    Open
572}
573
574impl WpaAuth {
575    ///Returns textual representation
576    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))]
589///Describes network's encryption method
590pub enum WpaEncryption {
591    ///No encryption
592    NONE,
593    ///Wired Equivalent Privacy
594    ///
595    ///Obsolete WPA's encryption
596    WEP,
597    ///Temporal Key Integrity Protocol
598    ///
599    ///Old WPA's encryption
600    TKIP,
601    ///Modern AES based encryption for WPA2
602    CCMP
603}
604
605impl WpaEncryption {
606    ///Returns textual representation
607    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    ///Indicates no encryption present
618    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))]
625///Network's flags in scan results
626pub struct WpaScanResultFlags {
627    ///Auth method
628    pub auth: WpaAuth,
629    ///Encryption method
630    pub encryption: WpaEncryption,
631}
632
633impl WpaScanResultFlags {
634    ///Converts flag string into this struct
635    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///Scan result's item description
674#[derive(Clone, Debug)]
675#[cfg_attr(feature = "serde", derive(serde_derive::Serialize, serde_derive::Deserialize))]
676pub struct WpaScanResult {
677    ///Network's BSSID. Can be empty string, when not set.
678    pub bssid: BssidStr,
679    ///Network's frequency
680    pub freq: u32,
681    ///Network's signal level in decibel(dBm)
682    pub level: i16,
683    ///Network's flag
684    pub flags: WpaScanResultFlags,
685    ///Network's SSID. Should not be empty normally
686    pub ssid: SsidStr,
687}
688
689impl WpaScanResult {
690    ///Returns signal level in percentage of range 0..=100
691    ///
692    ///Calculates as following: 2 * (dBm + 100)  where dBm: [-100 to -50]
693    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
704///Iterator over list of networks
705pub 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            //Assume quality is too bad if we cannot fit?
724            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
737///Message.
738pub struct WpaControlMessage<'a> {
739    ///Raw content of message
740    pub raw: &'a str,
741}
742
743impl<'a> WpaControlMessage<'a> {
744    #[inline(always)]
745    ///Returns whether message is unsolicited response, namely it means it is not reply to request.
746    pub const fn is_unsolicited(&self) -> bool {
747        !self.raw.is_empty() && self.raw.as_bytes()[0] == UNSOLICITED_PREFIX as u8
748    }
749
750    ///Attempts to reinterpret message as pong
751    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    ///Attempts to reinterpret message as success of request
760    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    ///Attempts to reinterpret message as failure of request
769    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    ///Attempts to reinterpret message as status
778    pub fn as_status(&self) -> Option<WpaStatus> {
779        WpaStatus::from_str(self.raw)
780    }
781
782    ///Attempts to reinterpret message as network id
783    pub fn as_network_id(&self) -> Option<Id> {
784        self.raw.parse().map(|id| Id(id)).ok()
785    }
786
787    ///Attempts to reinterpret message as network id
788    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    ///Attempts to reinterpret message as scan results
802    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        //bssid / frequency / signal level / flags / ssid
807        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
825///WPA controller
826///
827///All methods require to provide buffer to temporary hold data from the socket.
828///
829///Use `WpaController::into_buffered` to create wrapped instance with own buffer
830pub struct WpaController {
831    socket: UnixDatagram,
832    local: path::PathBuf,
833}
834
835impl WpaController {
836    #[inline(always)]
837    ///Attempts to connect to WPA controller at specified `path`
838    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    ///Attempts to connect to WPA controller at specified `path`
844    pub fn open_path(path: &path::Path) -> Result<Self, io::Error> {
845        Self::open_path_with(path, &DEFAULT_OPTIONS)
846    }
847
848    ///Attempts to connect to WPA controller at specified `path` with provided options
849    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        //Attempt to remove file, if for some reason it exists
854        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    ///Wraps controller with builtin buffer.
866    pub fn into_buffered(self) -> BufferedWpaController {
867        BufferedWpaController {
868            inner: self,
869            buffer: [0; BUF_SIZE],
870        }
871    }
872
873    #[inline]
874    ///Sends request, returning number of bytes written.
875    pub fn request(&self, req: WpaControlReq) -> Result<usize, io::Error> {
876        self.socket.send(req.buf.as_bytes())
877    }
878
879    ///Attempts to receive message.
880    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    ///Attempts to receive reply for result of command.
903    ///
904    ///This method will continuously `recv` skipping `unsolicited` messages
905    ///
906    ///# Result
907    ///
908    ///- Returns `None` if neither success or fail are present among replies.
909    ///
910    ///- `Ok(())` indicates success.
911    ///
912    ///- `Err(())` indicates failure.
913    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    ///Performs network add sequence
932    ///
933    ///# Arguments
934    ///
935    ///- `ssid` - Network identifier;
936    ///- `wpa_pass` - Passkey for WPA auth, if `None` sets `key_mgmt` to `None`
937    ///- `hidden` - Specifies whether you want to scan for SSID to connect to the network.
938    ///
939    ///# Result
940    ///
941    ///- `Ok(id)` - Newly created network id
942    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    ///Performs removal of known network by `id`.
997    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    ///Select a network for use by `id`.
1008    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    ///Reconfigure wpa, i.e. reload wpasupplicant from saved config.
1019    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
1038///WpaController with own buffer
1039pub struct BufferedWpaController {
1040    buffer: [u8; BUF_SIZE],
1041    inner: WpaController,
1042}
1043
1044impl BufferedWpaController {
1045    #[inline]
1046    ///Sends request, returning number of bytes written.
1047    pub fn request(&self, req: WpaControlReq) -> Result<usize, io::Error> {
1048        self.inner.socket.send(req.buf.as_bytes())
1049    }
1050
1051    #[inline]
1052    ///Attempts to receive message.
1053    pub fn recv(&mut self) -> Result<Option<WpaControlMessage<'_>>, io::Error> {
1054        self.inner.recv(&mut self.buffer)
1055    }
1056
1057    #[inline]
1058    ///Attempts to receive reply for result of command.
1059    ///
1060    ///This method will continuously `recv` skipping `unsolicited` messages
1061    ///
1062    ///# Result
1063    ///
1064    ///- Returns `None` if neither success or fail are present among replies.
1065    ///
1066    ///- `Ok(())` indicates success.
1067    ///
1068    ///- `Err(())` indicates failure.
1069    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    ///Performs network add sequence
1075    ///
1076    ///# Arguments
1077    ///
1078    ///- `ssid` - Network identifier;
1079    ///- `wpa_pass` - Passkey for WPA auth, if `None` sets `key_mgmt` to `None`
1080    ///- `hidden` - Specifies whether you want to scan for SSID to connect to the network.
1081    ///
1082    ///# Result
1083    ///
1084    ///- `Ok(id)` - Newly created network id
1085    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    ///Performs removal of known network by `id`.
1091    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    ///Select a network for use by `id`.
1097    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    ///Reconfigure wpa, i.e. reload wpasupplicant from saved config.
1103    pub fn reconfigure(&mut self) -> Result<(), io::Error> {
1104        self.inner.reconfigure(&mut self.buffer)
1105    }
1106}