rsubdomain 1.2.13

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::choose_probe_target;

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) -> Result<EthTable, String> {
    let (interface_name, src_ip, src_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 let Some(src_mac) = src_mac {
        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,
        });
    }

    info!(
        "自动检测网络设备成功: interface={} transport=udp src_ip={} (接口无MAC,切换为三层兼容模式)",
        interface_name, src_ip
    );

    Ok(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) {
        return Ok(mac);
    }

    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) {
            return Ok(mac);
        }
    }

    Err(format!(
        "无法解析下一跳 {} 在接口 {} 上的MAC地址",
        next_hop_ip, interface_name
    ))
}

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(())
}