rsubdomain 1.2.14

A high-performance subdomain brute-force tool written in Rust
Documentation
use log::{info, warn};
use pnet::datalink::{self, Channel::Ethernet, Config as DatalinkConfig, NetworkInterface};
use pnet::packet::arp::{ArpHardwareTypes, ArpOperations, ArpPacket, MutableArpPacket};
use pnet::packet::ethernet::{EtherTypes, EthernetPacket, MutableEthernetPacket};
use pnet::packet::{MutablePacket, Packet};
use pnet::util::MacAddr;
use std::io::ErrorKind;
use std::net::Ipv4Addr;
use std::time::{Duration, Instant};

use super::is_valid_next_hop_mac;

const ARP_FRAME_SIZE: usize = 42;
const ARP_TIMEOUT: Duration = Duration::from_millis(400);
const ARP_ATTEMPTS: usize = 3;

pub(super) fn resolve_mac_via_arp_probe(
    interface_name: &str,
    src_ip: Ipv4Addr,
    src_mac: MacAddr,
    target_ip: Ipv4Addr,
) -> Result<MacAddr, String> {
    info!(
        "开始主动ARP探测: interface={} src_ip={} src_mac={} target_ip={}",
        interface_name, src_ip, src_mac, target_ip
    );

    let interface = find_interface(interface_name)?;
    let config = DatalinkConfig {
        read_timeout: Some(ARP_TIMEOUT),
        write_timeout: Some(ARP_TIMEOUT),
        ..Default::default()
    };

    let (mut tx, mut rx) = match datalink::channel(&interface, config) {
        Ok(Ethernet(tx, rx)) => (tx, rx),
        Ok(_) => {
            return Err(format!(
                "接口 {} 返回了不支持的ARP数据链路通道类型",
                interface_name
            ));
        }
        Err(error) => {
            return Err(format!(
                "创建接口 {} 的ARP探测通道失败: {}",
                interface_name, error
            ));
        }
    };

    let request = build_arp_request(src_mac, src_ip, target_ip);

    for attempt in 0..ARP_ATTEMPTS {
        info!(
            "发送ARP探测请求: interface={} attempt={}/{} target_ip={}",
            interface_name,
            attempt + 1,
            ARP_ATTEMPTS,
            target_ip
        );

        match tx.send_to(&request, None) {
            Some(Ok(())) => {}
            Some(Err(error)) => {
                return Err(format!("发送ARP探测请求失败: {}", error));
            }
            None => return Err("ARP探测缺少目标网络接口".to_string()),
        }

        let deadline = Instant::now() + ARP_TIMEOUT;
        while Instant::now() < deadline {
            match rx.next() {
                Ok(frame) => {
                    if let Some(mac) = parse_arp_reply(frame, target_ip, src_ip) {
                        info!(
                            "主动ARP探测成功: interface={} target_ip={} resolved_mac={}",
                            interface_name, target_ip, mac
                        );
                        return Ok(mac);
                    }

                    if let Some(summary) = summarize_arp_frame(frame) {
                        warn!(
                            "ARP探测收到未命中的ARP帧: interface={} expected_sender_ip={} expected_target_ip={} observed={}",
                            interface_name,
                            target_ip,
                            src_ip,
                            summary
                        );
                    }
                }
                Err(error) if error.kind() == ErrorKind::TimedOut => {
                    warn!(
                        "ARP探测等待响应超时: interface={} attempt={}/{} target_ip={}",
                        interface_name,
                        attempt + 1,
                        ARP_ATTEMPTS,
                        target_ip
                    );
                    break;
                }
                Err(error) => return Err(format!("接收ARP探测响应失败: {}", error)),
            }
        }
    }

    Err(format!("主动ARP探测未收到 {} 的有效响应", target_ip))
}

fn find_interface(interface_name: &str) -> Result<NetworkInterface, String> {
    datalink::interfaces()
        .into_iter()
        .find(|iface| iface.name == interface_name && !iface.is_loopback())
        .ok_or_else(|| format!("未找到ARP探测所需网络接口 {}", interface_name))
}

fn build_arp_request(
    src_mac: MacAddr,
    src_ip: Ipv4Addr,
    target_ip: Ipv4Addr,
) -> [u8; ARP_FRAME_SIZE] {
    let mut buffer = [0u8; ARP_FRAME_SIZE];
    let mut ethernet_packet = MutableEthernetPacket::new(&mut buffer).unwrap();
    ethernet_packet.set_destination(MacAddr::broadcast());
    ethernet_packet.set_source(src_mac);
    ethernet_packet.set_ethertype(EtherTypes::Arp);

    let mut arp_packet = MutableArpPacket::new(ethernet_packet.payload_mut()).unwrap();
    arp_packet.set_hardware_type(ArpHardwareTypes::Ethernet);
    arp_packet.set_protocol_type(EtherTypes::Ipv4);
    arp_packet.set_hw_addr_len(6);
    arp_packet.set_proto_addr_len(4);
    arp_packet.set_operation(ArpOperations::Request);
    arp_packet.set_sender_hw_addr(src_mac);
    arp_packet.set_sender_proto_addr(src_ip);
    arp_packet.set_target_hw_addr(MacAddr::zero());
    arp_packet.set_target_proto_addr(target_ip);

    buffer
}

fn parse_arp_reply(
    frame: &[u8],
    expected_sender_ip: Ipv4Addr,
    expected_target_ip: Ipv4Addr,
) -> Option<MacAddr> {
    let ethernet = EthernetPacket::new(frame)?;
    if ethernet.get_ethertype() != EtherTypes::Arp {
        return None;
    }

    let arp = ArpPacket::new(ethernet.payload())?;
    if arp.get_operation() != ArpOperations::Reply {
        return None;
    }

    if arp.get_sender_proto_addr() != expected_sender_ip {
        return None;
    }

    if arp.get_target_proto_addr() != expected_target_ip {
        return None;
    }

    let sender_mac = arp.get_sender_hw_addr();
    if !is_valid_next_hop_mac(sender_mac) {
        return None;
    }

    Some(sender_mac)
}

fn summarize_arp_frame(frame: &[u8]) -> Option<String> {
    let ethernet = EthernetPacket::new(frame)?;
    if ethernet.get_ethertype() != EtherTypes::Arp {
        return None;
    }

    let arp = ArpPacket::new(ethernet.payload())?;
    Some(format!(
        "operation={:?} sender_ip={} sender_mac={} target_ip={} target_mac={}",
        arp.get_operation(),
        arp.get_sender_proto_addr(),
        arp.get_sender_hw_addr(),
        arp.get_target_proto_addr(),
        arp.get_target_hw_addr()
    ))
}

#[cfg(test)]
mod tests {
    use super::{build_arp_request, parse_arp_reply, summarize_arp_frame, ARP_FRAME_SIZE};
    use pnet::packet::arp::{ArpHardwareTypes, ArpOperations, MutableArpPacket};
    use pnet::packet::ethernet::{EtherTypes, MutableEthernetPacket};
    use pnet::packet::MutablePacket;
    use pnet::util::MacAddr;
    use std::net::Ipv4Addr;

    #[test]
    fn build_arp_request_targets_broadcast() {
        let frame = build_arp_request(
            MacAddr::new(0x10, 0x20, 0x30, 0x40, 0x50, 0x60),
            Ipv4Addr::new(10, 0, 0, 2),
            Ipv4Addr::new(10, 0, 0, 1),
        );

        let ethernet = MutableEthernetPacket::owned(frame.to_vec()).unwrap();
        assert_eq!(ethernet.get_ethertype(), EtherTypes::Arp);
        assert_eq!(ethernet.get_destination(), MacAddr::broadcast());
    }

    #[test]
    fn parse_arp_reply_accepts_matching_reply() {
        let mut frame = [0u8; ARP_FRAME_SIZE];
        let mut ethernet = MutableEthernetPacket::new(&mut frame).unwrap();
        ethernet.set_destination(MacAddr::new(0x10, 0x20, 0x30, 0x40, 0x50, 0x60));
        ethernet.set_source(MacAddr::new(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
        ethernet.set_ethertype(EtherTypes::Arp);

        let mut arp = MutableArpPacket::new(ethernet.payload_mut()).unwrap();
        arp.set_hardware_type(ArpHardwareTypes::Ethernet);
        arp.set_protocol_type(EtherTypes::Ipv4);
        arp.set_hw_addr_len(6);
        arp.set_proto_addr_len(4);
        arp.set_operation(ArpOperations::Reply);
        arp.set_sender_hw_addr(MacAddr::new(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
        arp.set_sender_proto_addr(Ipv4Addr::new(10, 0, 0, 1));
        arp.set_target_hw_addr(MacAddr::new(0x10, 0x20, 0x30, 0x40, 0x50, 0x60));
        arp.set_target_proto_addr(Ipv4Addr::new(10, 0, 0, 2));

        assert_eq!(
            parse_arp_reply(
                &frame,
                Ipv4Addr::new(10, 0, 0, 1),
                Ipv4Addr::new(10, 0, 0, 2)
            ),
            Some(MacAddr::new(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff))
        );
    }

    #[test]
    fn parse_arp_reply_accepts_zero_oui_unicast_mac() {
        let mut frame = [0u8; ARP_FRAME_SIZE];
        let mut ethernet = MutableEthernetPacket::new(&mut frame).unwrap();
        ethernet.set_destination(MacAddr::new(0x10, 0x20, 0x30, 0x40, 0x50, 0x60));
        ethernet.set_source(MacAddr::new(0, 0, 0, 0, 0, 1));
        ethernet.set_ethertype(EtherTypes::Arp);

        let mut arp = MutableArpPacket::new(ethernet.payload_mut()).unwrap();
        arp.set_hardware_type(ArpHardwareTypes::Ethernet);
        arp.set_protocol_type(EtherTypes::Ipv4);
        arp.set_hw_addr_len(6);
        arp.set_proto_addr_len(4);
        arp.set_operation(ArpOperations::Reply);
        arp.set_sender_hw_addr(MacAddr::new(0, 0, 0, 0, 0, 1));
        arp.set_sender_proto_addr(Ipv4Addr::new(10, 0, 0, 1));
        arp.set_target_hw_addr(MacAddr::new(0x10, 0x20, 0x30, 0x40, 0x50, 0x60));
        arp.set_target_proto_addr(Ipv4Addr::new(10, 0, 0, 2));

        assert_eq!(
            parse_arp_reply(
                &frame,
                Ipv4Addr::new(10, 0, 0, 1),
                Ipv4Addr::new(10, 0, 0, 2)
            ),
            Some(MacAddr::new(0, 0, 0, 0, 0, 1))
        );
    }

    #[test]
    fn summarize_arp_frame_reports_key_fields() {
        let mut frame = [0u8; ARP_FRAME_SIZE];
        let mut ethernet = MutableEthernetPacket::new(&mut frame).unwrap();
        ethernet.set_destination(MacAddr::broadcast());
        ethernet.set_source(MacAddr::new(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
        ethernet.set_ethertype(EtherTypes::Arp);

        let mut arp = MutableArpPacket::new(ethernet.payload_mut()).unwrap();
        arp.set_hardware_type(ArpHardwareTypes::Ethernet);
        arp.set_protocol_type(EtherTypes::Ipv4);
        arp.set_hw_addr_len(6);
        arp.set_proto_addr_len(4);
        arp.set_operation(ArpOperations::Reply);
        arp.set_sender_hw_addr(MacAddr::new(0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff));
        arp.set_sender_proto_addr(Ipv4Addr::new(10, 0, 0, 1));
        arp.set_target_hw_addr(MacAddr::new(0x10, 0x20, 0x30, 0x40, 0x50, 0x60));
        arp.set_target_proto_addr(Ipv4Addr::new(10, 0, 0, 2));

        let summary = summarize_arp_frame(&frame).unwrap();

        assert!(summary.contains("sender_ip=10.0.0.1"));
        assert!(summary.contains("target_ip=10.0.0.2"));
    }
}