Skip to content

Instantly share code, notes, and snippets.

@InternetUnexplorer
Created May 9, 2025 22:28
Show Gist options
  • Save InternetUnexplorer/17d0a54fec09130ac9ad315c5d99cd4a to your computer and use it in GitHub Desktop.
Save InternetUnexplorer/17d0a54fec09130ac9ad315c5d99cd4a to your computer and use it in GitHub Desktop.
Rust module to resolve Tailscale IP addresses to names
use std::collections::HashMap;
use std::net::IpAddr;
use std::sync::{LazyLock, Mutex};
use anyhow::{Context, Result, anyhow};
use tokio::process::Command;
use tracing::debug;
static CACHE: LazyLock<Mutex<HashMap<IpAddr, String>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
fn cache_get(addr: IpAddr) -> Option<String> {
let cache = CACHE.lock().unwrap();
cache.get(&addr).cloned()
}
fn cache_put(addr: IpAddr, name: String) {
let mut cache = CACHE.lock().unwrap();
cache.insert(addr, name);
}
async fn lookup(addr: IpAddr) -> Result<String> {
let output = Command::new("tailscale")
.arg("whois")
.arg(addr.to_string())
.output()
.await
.context("failed to spawn process")?;
String::from_utf8_lossy(&output.stdout)
.lines()
.filter_map(|line| line.strip_prefix(" Name:"))
.map(|name| name.trim().to_string())
.next()
.ok_or_else(|| anyhow!("failed to parse output"))
}
/// Resolve a [`std::net::IpAddr`] to a hostname using `tailscale whois`.
pub async fn whois(addr: IpAddr) -> Result<String> {
if let Some(name) = cache_get(addr) {
debug!("resolved {} -> {} (cached)", addr, name);
return Ok(name);
}
let name = lookup(addr).await.context("error resolving host")?;
cache_put(addr, name.clone());
debug!("resolved {} -> {}", addr, name);
Ok(name)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment