rsubdomain 1.2.14

A high-performance subdomain brute-force tool written in Rust
Documentation
use log::info;
use pnet::datalink;
use std::net::{IpAddr, Ipv4Addr, SocketAddrV4, UdpSocket};
use std::thread;
use std::time::Duration;

use crate::device::{lookup_arp_cache, resolve_route_to_target};
use crate::model::{EthTable, PacketTransport};

use super::{arp::resolve_mac_via_arp_probe, choose_probe_target, is_valid_next_hop_mac};

pub(super) fn default_probe_target() -> Ipv4Addr {
    choose_probe_target(&[]).unwrap_or(Ipv4Addr::new(223, 5, 5, 5))
}

pub(super) fn resolve_best_device(
    probe_target: Ipv4Addr,
    transport: PacketTransport,
) -> Result<EthTable, String> {
    let (interface_name, src_ip, interface_mac) = detect_egress_interface(probe_target)?;
    let route = resolve_route_to_target(probe_target)?;

    if let Some(route_interface) = route.interface.as_ref() {
        if route_interface != &interface_name {
            return Err(format!(
                "系统路由接口 {} 与本地出接口 {} 不一致",
                route_interface, interface_name
            ));
        }
    }

    if transport == PacketTransport::Udp {
        let _ = interface_mac;
        info!(
            "自动检测网络设备成功: interface={} transport=udp src_ip={}",
            interface_name, src_ip
        );
        return Ok(build_udp_compatible_device(src_ip, interface_name));
    }

    {
        let src_mac = interface_mac.ok_or_else(|| {
            format!(
                "接口 {} 缺少MAC地址,无法进行二层以太网发包",
                interface_name
            )
        })?;
        let next_hop_ip = route.gateway.unwrap_or(probe_target);
        let dst_mac = resolve_next_hop_mac(src_ip, probe_target, next_hop_ip, &interface_name)?;

        info!(
            "自动检测网络设备成功: interface={} transport=ethernet src_ip={} next_hop={} dst_mac={}",
            interface_name, src_ip, next_hop_ip, dst_mac
        );

        return Ok(EthTable {
            src_ip,
            device: interface_name,
            src_mac,
            dst_mac,
            transport: PacketTransport::Ethernet,
        });
    }
}

pub(super) fn build_udp_compatible_device(src_ip: Ipv4Addr, interface_name: String) -> EthTable {
    EthTable {
        src_ip,
        device: interface_name,
        src_mac: pnet::util::MacAddr::zero(),
        dst_mac: pnet::util::MacAddr::zero(),
        transport: PacketTransport::Udp,
    }
}

pub(super) fn resolve_next_hop_mac_on_interface(
    src_ip: Ipv4Addr,
    probe_target: Ipv4Addr,
    next_hop_ip: Ipv4Addr,
    interface_name: &str,
) -> Result<pnet::util::MacAddr, String> {
    resolve_next_hop_mac(src_ip, probe_target, next_hop_ip, interface_name)
}

pub(super) fn first_ipv4_on_interface(interface: &datalink::NetworkInterface) -> Option<Ipv4Addr> {
    interface.ips.iter().find_map(|network| match network.ip() {
        IpAddr::V4(ip) => Some(ip),
        IpAddr::V6(_) => None,
    })
}

fn detect_egress_interface(
    probe_target: Ipv4Addr,
) -> Result<(String, Ipv4Addr, Option<pnet::util::MacAddr>), String> {
    let socket = UdpSocket::bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0))
        .map_err(|error| format!("绑定UDP探测socket失败: {}", error))?;
    socket
        .connect(SocketAddrV4::new(probe_target, 53))
        .map_err(|error| format!("连接探测DNS服务器失败: {}", error))?;

    let local_ip = match socket
        .local_addr()
        .map_err(|error| format!("读取本地探测地址失败: {}", error))?
        .ip()
    {
        IpAddr::V4(ip) => ip,
        IpAddr::V6(ip) => {
            return Err(format!("探测得到IPv6地址 {},当前仅支持IPv4原始发包", ip));
        }
    };

    for interface in datalink::interfaces() {
        if interface.is_loopback() {
            continue;
        }

        let has_local_ip = interface
            .ips
            .iter()
            .any(|network| network.ip() == IpAddr::V4(local_ip));
        if !has_local_ip {
            continue;
        }

        return Ok((interface.name, local_ip, interface.mac));
    }

    Err(format!("未找到承载本地地址 {} 的有效网络接口", local_ip))
}

fn resolve_next_hop_mac(
    src_ip: Ipv4Addr,
    probe_target: Ipv4Addr,
    next_hop_ip: Ipv4Addr,
    interface_name: &str,
) -> Result<pnet::util::MacAddr, String> {
    if let Some(mac) = lookup_arp_cache(next_hop_ip, interface_name) {
        if is_valid_next_hop_mac(mac) {
            return Ok(mac);
        }

        return Err(format!(
            "ARP缓存返回了无效的下一跳MAC {} (next_hop={} interface={})",
            mac, next_hop_ip, interface_name
        ));
    }

    let arp_probe_error = if let Some(src_mac) = interface_mac(interface_name) {
        match resolve_mac_via_arp_probe(interface_name, src_ip, src_mac, next_hop_ip) {
            Ok(mac) => return Ok(mac),
            Err(error) => error,
        }
    } else {
        format!("接口 {} 没有可用源MAC,无法执行主动ARP探测", interface_name)
    };

    for attempt in 0..5 {
        warm_up_neighbor_cache(src_ip, probe_target)
            .map_err(|error| format!("预热ARP缓存失败: {}", error))?;
        thread::sleep(Duration::from_millis(200 * (attempt + 1) as u64));

        if let Some(mac) = lookup_arp_cache(next_hop_ip, interface_name) {
            if is_valid_next_hop_mac(mac) {
                return Ok(mac);
            }

            return Err(format!(
                "ARP缓存返回了无效的下一跳MAC {} (next_hop={} interface={})",
                mac, next_hop_ip, interface_name
            ));
        }
    }

    Err(format!(
        "无法解析下一跳 {} 在接口 {} 上的MAC地址; 主动ARP探测详情: {}",
        next_hop_ip, interface_name, arp_probe_error
    ))
}

fn interface_mac(interface_name: &str) -> Option<pnet::util::MacAddr> {
    datalink::interfaces()
        .into_iter()
        .find(|iface| iface.name == interface_name && !iface.is_loopback())
        .and_then(|iface| iface.mac)
}

fn warm_up_neighbor_cache(src_ip: Ipv4Addr, probe_target: Ipv4Addr) -> Result<(), std::io::Error> {
    let socket = UdpSocket::bind(SocketAddrV4::new(src_ip, 0))?;
    socket.set_write_timeout(Some(Duration::from_millis(500)))?;
    let _ = socket.send_to(&[0u8], SocketAddrV4::new(probe_target, 53));
    Ok(())
}