rsubdomain 1.2.14

A high-performance subdomain brute-force tool written in Rust
Documentation
use std::net::Ipv4Addr;
use std::process::Command;
use std::str::FromStr;

use super::common::{parse_mac_addr, RouteResolution};

pub(super) fn resolve_route_to_target(probe_target: Ipv4Addr) -> Result<RouteResolution, String> {
    let output = Command::new("ip")
        .arg("route")
        .arg("get")
        .arg(probe_target.to_string())
        .output()
        .map_err(|error| format!("执行 ip route get 失败: {}", error))?;

    if !output.status.success() {
        return Err(format!(
            "ip route get 返回失败: {}",
            String::from_utf8_lossy(&output.stderr).trim()
        ));
    }

    let stdout = String::from_utf8_lossy(&output.stdout);
    let tokens: Vec<&str> = stdout.split_whitespace().collect();
    let interface = token_after(&tokens, "dev").map(|value| value.to_string());
    let gateway = token_after(&tokens, "via").and_then(|value| Ipv4Addr::from_str(value).ok());

    Ok(RouteResolution { interface, gateway })
}

pub(super) fn lookup_arp_cache(
    target_ip: Ipv4Addr,
    interface_name: &str,
) -> Option<pnet::util::MacAddr> {
    let output = Command::new("ip")
        .arg("neigh")
        .arg("show")
        .arg(target_ip.to_string())
        .arg("dev")
        .arg(interface_name)
        .output()
        .ok()?;

    if !output.status.success() {
        return None;
    }

    let stdout = String::from_utf8_lossy(&output.stdout);
    parse_ip_neigh_output(&stdout)
}

fn parse_ip_neigh_output(output: &str) -> Option<pnet::util::MacAddr> {
    for line in output.lines() {
        let tokens: Vec<&str> = line.split_whitespace().collect();
        let mac_text = token_after(&tokens, "lladdr")?;
        if mac_text == "FAILED" {
            continue;
        }
        if let Some(mac) = parse_mac_addr(mac_text) {
            return Some(mac);
        }
    }

    None
}

fn token_after<'a>(tokens: &'a [&'a str], key: &str) -> Option<&'a str> {
    tokens
        .iter()
        .position(|token| *token == key)
        .and_then(|index| tokens.get(index + 1).copied())
}