Commit 8fe6832a authored by Max Verevkin's avatar Max Verevkin
Browse files

add async feature

parent 07b0bc4d
/target
**/*.rs.bk
Cargo.lock
/examples/target
......@@ -8,6 +8,14 @@ repository = "https://github.com/MaxVerevkin/neli-wifi"
license = "MIT"
keywords = ["wifi", "neli", "netlink", "nl80211"]
[features]
default = []
async = ["neli/async"]
[dependencies]
neli = "0.6.0"
neli-proc-macros = "0.1.0"
[dev-dependencies.tokio]
version = "1.0"
features = ["macros"]
[package]
name = "examples"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "show_interfaces"
path = "show_interfaces.rs"
[[bin]]
name = "show_interfaces_async"
path = "show_interfaces_async.rs"
[dependencies.neli-wifi]
path = "../"
features = ["async"]
[dependencies.tokio]
version = "1.0"
features = ["macros"]
//! Pretty print host interfaces.
use neli_wifi::AsyncSocket;
use std::error::Error;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let mut socket = AsyncSocket::connect()?;
for interface in socket.get_interfaces_info().await? {
dbg!(&interface);
if let Some(index) = &interface.index {
dbg!(socket.get_station_info(index).await?);
dbg!(socket.get_bss_info(index).await?);
}
eprintln!("---");
}
Ok(())
}
use crate::Bss;
use crate::Interface;
use crate::Nl80211Attr;
use crate::Nl80211Cmd;
use crate::Socket;
use crate::Station;
use crate::NL_80211_GENL_VERSION;
use neli::consts::genl::{CtrlAttr, CtrlCmd};
use neli::consts::{nl::GenlId, nl::NlmF, nl::NlmFFlags, nl::Nlmsg};
use neli::err::NlError;
use neli::genl::{Genlmsghdr, Nlattr};
use neli::nl::{NlPayload, Nlmsghdr};
use neli::socket::tokio::NlSocket;
use neli::types::GenlBuffer;
/// A generic netlink socket to send commands and receive messages
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub struct AsyncSocket {
sock: NlSocket,
family_id: u16,
}
impl TryFrom<Socket> for AsyncSocket {
type Error = std::io::Error;
fn try_from(from: Socket) -> Result<Self, Self::Error> {
Ok(Self {
sock: NlSocket::new(from.sock)?,
family_id: from.family_id,
})
}
}
impl AsyncSocket {
/// Create a new nl80211 socket with netlink
pub fn connect() -> Result<Self, NlError<GenlId, Genlmsghdr<CtrlCmd, CtrlAttr>>> {
Ok(Socket::connect()?.try_into()?)
}
/// Get information for all your wifi interfaces
///
/// # Example
///
/// ```no_run
/// # use neli_wifi::AsyncSocket;
/// # use std::error::Error;
///
/// # async fn test() -> Result<(), Box<dyn Error>>{
/// let wifi_interfaces = AsyncSocket::connect()?.get_interfaces_info().await?;
/// for wifi_interface in wifi_interfaces {
/// println!("{:#?}", wifi_interface);
/// }
/// # Ok(())
/// # };
///```
pub async fn get_interfaces_info(&mut self) -> Result<Vec<Interface>, NlError> {
let msghdr = Genlmsghdr::<Nl80211Cmd, Nl80211Attr>::new(
Nl80211Cmd::CmdGetInterface,
NL_80211_GENL_VERSION,
GenlBuffer::new(),
);
let nlhdr = {
let len = None;
let nl_type = self.family_id;
let flags = NlmFFlags::new(&[NlmF::Request, NlmF::Dump]);
let seq = None;
let pid = None;
let payload = NlPayload::Payload(msghdr);
Nlmsghdr::new(len, nl_type, flags, seq, pid, payload)
};
self.sock.send(&nlhdr).await?;
let mut buf = Vec::new();
let mut interfaces = Vec::new();
loop {
let res = self
.sock
.recv::<Nlmsg, Genlmsghdr<Nl80211Cmd, Nl80211Attr>>(&mut buf)
.await?;
for response in res {
match response.nl_type {
Nlmsg::Noop => (),
Nlmsg::Error => panic!("Error"),
Nlmsg::Done => return Ok(interfaces),
_ => {
let handle = response.nl_payload.get_payload().unwrap().get_attr_handle();
interfaces.push(handle.try_into()?);
}
};
}
}
}
/// Get access point information for a specific interface
///
/// # Example
///
/// ```no_run
/// # use neli_wifi::AsyncSocket;
/// # use std::error::Error;
///
/// # async fn test() -> Result<(), Box<dyn Error>> {
/// let mut socket = AsyncSocket::connect()?;
/// // First of all we need to get wifi interface information to get more data
/// let wifi_interfaces = socket.get_interfaces_info().await?;
/// for wifi_interface in wifi_interfaces {
/// if let Some(netlink_index) = wifi_interface.index {
/// // Then for each wifi interface we can fetch station information
/// let station_info = socket.get_station_info(&netlink_index.clone()).await?;
/// println!("{:#?}", station_info);
/// }
/// }
/// # Ok(())
/// # }
///```
pub async fn get_station_info(
&mut self,
interface_attr_if_index: &[u8],
) -> Result<Station, NlError> {
let msghdr = Genlmsghdr::<Nl80211Cmd, Nl80211Attr>::new(
Nl80211Cmd::CmdGetStation,
NL_80211_GENL_VERSION,
{
let mut attrs = GenlBuffer::new();
attrs.push(
Nlattr::new(
false,
false,
Nl80211Attr::AttrIfindex,
NlPayload::<(), Vec<u8>>::Payload(interface_attr_if_index.to_owned()),
)
.unwrap(),
);
attrs
},
);
let nlhdr = {
let len = None;
let nl_type = self.family_id;
let flags = NlmFFlags::new(&[NlmF::Request, NlmF::Dump]);
let seq = None;
let pid = None;
let payload = NlPayload::Payload(msghdr);
Nlmsghdr::new(len, nl_type, flags, seq, pid, payload)
};
self.sock.send(&nlhdr).await?;
let mut buf = Vec::new();
let mut retval = None;
loop {
let res = self
.sock
.recv::<Nlmsg, Genlmsghdr<Nl80211Cmd, Nl80211Attr>>(&mut buf)
.await?;
for response in res {
match response.nl_type {
Nlmsg::Noop => (),
Nlmsg::Error => panic!("Error"),
Nlmsg::Done => return Ok(retval.unwrap_or_default()),
_ => {
retval = Some(
response
.nl_payload
.get_payload()
.unwrap()
.get_attr_handle()
.try_into()?,
);
}
};
}
}
}
pub async fn get_bss_info(&mut self, interface_attr_if_index: &[u8]) -> Result<Bss, NlError> {
let msghdr = Genlmsghdr::<Nl80211Cmd, Nl80211Attr>::new(
Nl80211Cmd::CmdGetScan,
NL_80211_GENL_VERSION,
{
let mut attrs = GenlBuffer::new();
attrs.push(
Nlattr::new(
false,
false,
Nl80211Attr::AttrIfindex,
NlPayload::<(), Vec<u8>>::Payload(interface_attr_if_index.to_owned()),
)
.unwrap(),
);
attrs
},
);
let nlhdr = {
let len = None;
let nl_type = self.family_id;
let flags = NlmFFlags::new(&[NlmF::Request, NlmF::Dump]);
let seq = None;
let pid = None;
let payload = NlPayload::Payload(msghdr);
Nlmsghdr::new(len, nl_type, flags, seq, pid, payload)
};
self.sock.send(&nlhdr).await?;
let mut buf = Vec::new();
let mut retval = None;
loop {
let res = self
.sock
.recv::<Nlmsg, Genlmsghdr<Nl80211Cmd, Nl80211Attr>>(&mut buf)
.await?;
for response in res {
match response.nl_type {
Nlmsg::Noop => (),
Nlmsg::Error => panic!("Error"),
Nlmsg::Done => return Ok(retval.unwrap_or_default()),
_ => {
retval = Some(
response
.nl_payload
.get_payload()
.unwrap()
.get_attr_handle()
.try_into()?,
);
}
}
}
}
}
}
impl From<AsyncSocket> for NlSocket {
/// Returns the underlying generic netlink socket
fn from(sock: AsyncSocket) -> Self {
sock.sock
}
}
......@@ -3,7 +3,7 @@ use neli_proc_macros::neli_enum;
/// nl80211Commands
///
/// https://github.com/mdlayher/wifi/blob/b1436901ddee2ea3ee8782a440a084e457615766/internal/nl80211/const.go
/// <https://github.com/mdlayher/wifi/blob/b1436901ddee2ea3ee8782a440a084e457615766/internal/nl80211/const.go>
///
/// Enumeration from nl80211/nl80211.h:880
#[neli_enum(serialized_type = "u8")]
......
......@@ -17,4 +17,9 @@ mod interface;
pub use interface::*;
mod socket;
pub use socket::Socket;
pub use socket::*;
#[cfg(feature = "async")]
mod async_socket;
#[cfg(feature = "async")]
pub use async_socket::*;
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment