in linux, use resolvectl or /etc/resolv.conf for dns resolving
This commit is contained in:
parent
fad345becb
commit
b343124821
@ -1,6 +1,7 @@
|
||||
use punchnet::get_base_dir;
|
||||
use punchnet::get_edge;
|
||||
use punchnet::get_my_networks;
|
||||
use punchnet::restore_dns;
|
||||
use punchnet::run_sdlan;
|
||||
use punchnet::set_base_dir;
|
||||
use punchnet::CommandLine;
|
||||
@ -16,19 +17,12 @@ use structopt::StructOpt;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// let _guard = rolling_file::new_log(
|
||||
// "./.output",
|
||||
// 7,
|
||||
// rolling_file::PeriodGap::Daily,
|
||||
// tracing::Level::DEBUG,
|
||||
// true,
|
||||
// true,
|
||||
// );
|
||||
set_base_dir("/usr/local/punchnet");
|
||||
let _guard = log::init_log(&format!("{}/.output", get_base_dir()));
|
||||
|
||||
let cmd = CommandLineInput::from_args();
|
||||
|
||||
|
||||
// println!("port is {}", cmd.port);
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
@ -116,16 +110,29 @@ async fn main() {
|
||||
|
||||
let mut started = true;
|
||||
*/
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
/*
|
||||
let sig = stream.recv().await;
|
||||
if started {
|
||||
edge.stop().await;
|
||||
} else {
|
||||
edge.start("0".to_owned()).await;
|
||||
|
||||
match tokio::signal::ctrl_c().await {
|
||||
Ok(()) => {
|
||||
restore_dns();
|
||||
println!("restoreing dns");
|
||||
}
|
||||
started = !started;
|
||||
*/
|
||||
Err(err) => {
|
||||
eprintln!("failed to listen for shutdown signal: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
|
||||
// loop {
|
||||
// tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
// /*
|
||||
// let sig = stream.recv().await;
|
||||
// if started {
|
||||
// edge.stop().await;
|
||||
// } else {
|
||||
// edge.start("0".to_owned()).await;
|
||||
// }
|
||||
// started = !started;
|
||||
// */
|
||||
// }
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ use std::net::{SocketAddr, ToSocketAddrs};
|
||||
|
||||
pub use network::get_edge;
|
||||
pub use network::get_install_channel;
|
||||
use network::{async_main, init_arp, init_edge, NodeConfig};
|
||||
pub use network::{async_main, init_arp, init_edge, NodeConfig, restore_dns};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::sync::mpsc::{channel, Sender};
|
||||
|
||||
@ -83,7 +83,7 @@ async fn handle_tcp_message(msg: SdlanTcp) {
|
||||
.ip
|
||||
.net_bit_len
|
||||
.store(dev.net_bit_len as u8, Ordering::Relaxed);
|
||||
edge.device.reload_config(&edge.device_config);
|
||||
edge.device.reload_config(&edge.device_config, &dev.network_domain);
|
||||
edge.network_id.store(dev.network_id, Ordering::Relaxed);
|
||||
|
||||
edge.set_authorized(true, aes);
|
||||
@ -578,8 +578,9 @@ async fn loop_tap(eee: &'static Node, cancel: CancellationToken) {
|
||||
packet.push(0x08);
|
||||
packet.push(0x00);
|
||||
packet.extend_from_slice(&reply);
|
||||
eee.device.handle_packet_from_net(&packet, &Vec::new()).await;
|
||||
println!("got 15353's reply");
|
||||
if let Err(_e) = eee.device.handle_packet_from_net(&packet, &Vec::new()).await {
|
||||
error!("failed to write dns packet to device");
|
||||
}
|
||||
}
|
||||
buf = rx.recv() => {
|
||||
if buf.is_none() {
|
||||
|
||||
@ -21,6 +21,6 @@ pub use tuntap::*;
|
||||
#[cfg_attr(target_os = "linux", path = "tun_linux.rs")]
|
||||
#[cfg_attr(target_os = "windows", path = "tun_win.rs")]
|
||||
mod tun;
|
||||
pub use tun::get_install_channel;
|
||||
pub use tun::{get_install_channel, restore_dns};
|
||||
|
||||
mod device;
|
||||
|
||||
@ -6,12 +6,14 @@ use sdlan_sn_rs::utils::{
|
||||
};
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::{c_char, c_int};
|
||||
use std::fs::OpenOptions;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
use std::path::Path;
|
||||
use std::ptr::null_mut;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use sdlan_sn_rs::utils::Result;
|
||||
use std::io::{Read, Write};
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::process::Command;
|
||||
|
||||
@ -27,6 +29,9 @@ use super::TunTapPacketHandler;
|
||||
|
||||
const DNS_IP: u32 = (100<<24) + (100<<16) + (100<<8) + 100;
|
||||
|
||||
const RESOLV_FILE: &'static str = "/etc/resolv.conf";
|
||||
const RESOLV_FILE_BACKUP: &'static str = "/etc/resolv.conf.punchnet.bak";
|
||||
|
||||
// #[link(name = "tuntap", kind="static")]
|
||||
#[link(name = "tuntap")]
|
||||
extern "C" {
|
||||
@ -38,6 +43,7 @@ pub struct Iface {
|
||||
fd: std::fs::File,
|
||||
mode: Mode,
|
||||
name: String,
|
||||
has_resolvectl: bool,
|
||||
}
|
||||
|
||||
pub fn new_iface(tunname: &str, mode: Mode) -> Iface {
|
||||
@ -72,7 +78,7 @@ impl Iface {
|
||||
let mut success = false;
|
||||
let mut _name = Vec::new();
|
||||
for i in 0..16 {
|
||||
_name = Vec::new();
|
||||
_name.clear();
|
||||
_name.extend_from_slice(ifname.as_bytes());
|
||||
_name.extend_from_slice(i.to_string().as_bytes());
|
||||
_name.extend_from_slice(&[0; 33]);
|
||||
@ -98,25 +104,30 @@ impl Iface {
|
||||
.to_string_lossy()
|
||||
.into_owned()
|
||||
};
|
||||
Ok(Iface { fd: fs, mode, name })
|
||||
let has_resolvectl = check_has_resolvectl();
|
||||
|
||||
Ok(Iface { fd: fs, mode, name, has_resolvectl })
|
||||
} else {
|
||||
Err(SDLanError::NormalError("failed to setup tun"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reload_config(&self, device_config: &DeviceConfig) {
|
||||
pub fn reload_config(&self, device_config: &DeviceConfig, network_domain: &str) {
|
||||
let netbit = device_config.get_net_bit();
|
||||
let ip = device_config.get_ip();
|
||||
if netbit == 0 || ip == 0 {
|
||||
error!("reload config's ip is 0");
|
||||
return;
|
||||
}
|
||||
let mask = net_bit_len_to_mask(netbit);
|
||||
let default_gw = (ip & mask) + 1;
|
||||
let ip = ip_to_string(&ip);
|
||||
let netbit = ip_to_string(&net_bit_len_to_mask(netbit));
|
||||
|
||||
if cfg!(not(feature = "tun")) {
|
||||
info!("set tap device");
|
||||
let mac = device_config.get_mac();
|
||||
|
||||
let res = Command::new("ifconfig")
|
||||
.arg(&self.name)
|
||||
.arg(ip)
|
||||
@ -140,6 +151,13 @@ impl Iface {
|
||||
error!("failed to run ifconfig: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = set_dns(self, &self.name, network_domain, &ip_to_string(&default_gw)) {
|
||||
println!("failed to set dns: {}", e.as_str());
|
||||
error!("failed to set dns: {}", e.as_str());
|
||||
} else {
|
||||
println!("set dns ok");
|
||||
}
|
||||
} else {
|
||||
info!("set tun device");
|
||||
let res = Command::new("ifconfig")
|
||||
@ -159,6 +177,13 @@ impl Iface {
|
||||
error!("failed to run ifconfig: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = set_dns(self, &self.name, network_domain, &ip_to_string(&default_gw)) {
|
||||
println!("failed to set dns: {}", e.as_str());
|
||||
error!("failed to set dns: {}", e.as_str());
|
||||
} else {
|
||||
println!("set dns ok");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -519,3 +544,214 @@ impl TunTapPacketHandler for Iface {
|
||||
pub fn get_install_channel() -> String {
|
||||
"linux".to_owned()
|
||||
}
|
||||
|
||||
fn check_has_resolvectl() -> bool {
|
||||
return false;
|
||||
let res = Command::new("resolvectl")
|
||||
.arg("status")
|
||||
.output();
|
||||
if let Ok(_) = res {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn add_dns_route(gw: &str) -> Result<()>{
|
||||
Command::new("route")
|
||||
.arg("add")
|
||||
.arg("-host")
|
||||
.arg("100.100.100.100")
|
||||
.arg("gw")
|
||||
.arg(gw)
|
||||
.output()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_resolvectl(
|
||||
name: &str,
|
||||
network_domain: &str,
|
||||
) -> Result<()>{
|
||||
Command::new("resolvectl")
|
||||
.arg("dns")
|
||||
.arg(name)
|
||||
.arg("100.100.100.100")
|
||||
.output()?;
|
||||
|
||||
Command::new("resolvectl")
|
||||
.arg("domain")
|
||||
.arg(name)
|
||||
.arg(format!("~{}", network_domain))
|
||||
.output()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_dns(
|
||||
iface: &Iface,
|
||||
name: &str,
|
||||
network_domain: &str,
|
||||
gw: &str
|
||||
) -> Result<()> {
|
||||
if iface.has_resolvectl {
|
||||
add_resolvectl(name, network_domain)?;
|
||||
} else {
|
||||
backup_resolv_conf()?;
|
||||
modify_resolv_conf(&vec!["100.100.100.100".to_owned()], true)?;
|
||||
}
|
||||
add_dns_route(gw)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn restore_dns() -> Result<()> {
|
||||
let eee = get_edge();
|
||||
if !eee.device.has_resolvectl {
|
||||
// should restore /etc/resolv.conf
|
||||
restore_resolv_conf()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 修改 /etc/resolv.conf 中的 nameserver 条目
|
||||
///
|
||||
/// - `new_nameservers`: 新的 nameserver 列表(IPv4/IPv6 字符串)
|
||||
/// - `keep_other_ns`: 是否保留原有的 nameserver(true = 追加到新列表后,false = 完全替换)
|
||||
pub fn modify_resolv_conf(new_nameservers: &[String], keep_other_ns: bool) -> Result<()> {
|
||||
let path = Path::new(RESOLV_FILE);
|
||||
if !path.exists() {
|
||||
return Err(SDLanError::IOError(format!("{} does not exists", RESOLV_FILE)));
|
||||
}
|
||||
|
||||
// 读取原文件权限和元数据
|
||||
let metadata = fs::metadata(path)?;
|
||||
let perms = fs::Permissions::from_mode(metadata.mode());
|
||||
let uid = metadata.uid();
|
||||
let gid = metadata.gid();
|
||||
|
||||
// 读取所有行
|
||||
let file = fs::File::open(path)?;
|
||||
let reader = BufReader::new(file);
|
||||
let mut lines = Vec::new();
|
||||
|
||||
let mut inserted = false;
|
||||
let mut encounted_nameserver = false;
|
||||
|
||||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
let trimmed = line.trim();
|
||||
if trimmed.starts_with("nameserver ") {
|
||||
// 提取 IP
|
||||
encounted_nameserver = true;
|
||||
if keep_other_ns {
|
||||
lines.push(line);
|
||||
}
|
||||
// 如果不保留原有 nameserver,就不加入 lines
|
||||
} else {
|
||||
if encounted_nameserver {
|
||||
if !inserted {
|
||||
inserted = true;
|
||||
for new in new_nameservers {
|
||||
lines.push(format!("nameserver {}", new.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// 保留非 nameserver 行(注释、search、options 等)
|
||||
lines.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
// 原子写入:先写临时文件
|
||||
let tmp_dir = Path::new("/etc");
|
||||
let tmp_path = tmp_dir.join("resolv.conf.tmp");
|
||||
let mut tmp_file = fs::File::create(&tmp_path)?;
|
||||
println!("new resolv.conf: ");
|
||||
for l in &lines {
|
||||
writeln!(tmp_file, "{}", l)?;
|
||||
println!("{}", l);
|
||||
}
|
||||
println!("");
|
||||
tmp_file.flush()?;
|
||||
|
||||
// 设置权限
|
||||
fs::set_permissions(&tmp_path, perms)?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::chown;
|
||||
chown(&tmp_path, Some(uid), Some(gid))?;
|
||||
}
|
||||
|
||||
// 原子替换
|
||||
fs::rename(&tmp_path, path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 备份 /etc/resolv.conf 到指定路径
|
||||
fn backup_resolv_conf() -> Result<()> {
|
||||
let src = Path::new(RESOLV_FILE);
|
||||
let dst = Path::new(RESOLV_FILE_BACKUP);
|
||||
|
||||
if !src.exists() {
|
||||
return Err(SDLanError::IOError(format!("{} does not exists", RESOLV_FILE)));
|
||||
// anyhow::bail!("Source /etc/resolv.conf does not exist");
|
||||
}
|
||||
|
||||
// 如果目标已存在,可选择覆盖或跳过(这里覆盖)
|
||||
if dst.exists() {
|
||||
fs::remove_file(dst)?;
|
||||
}
|
||||
|
||||
fs::copy(src, dst)?;
|
||||
|
||||
// 保留原权限
|
||||
let metadata = fs::metadata(src)?;
|
||||
let permissions = fs::Permissions::from_mode(metadata.mode());
|
||||
fs::set_permissions(dst, permissions)?;
|
||||
|
||||
// 保留所有者(需要 root 权限,否则会失败)
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::chown;
|
||||
let uid = metadata.uid();
|
||||
let gid = metadata.gid();
|
||||
chown(dst, Some(uid), Some(gid))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 从备份路径恢复 /etc/resolv.conf
|
||||
fn restore_resolv_conf() -> Result<()> {
|
||||
let src = Path::new(RESOLV_FILE_BACKUP);
|
||||
let dst = Path::new(RESOLV_FILE);
|
||||
|
||||
if !src.exists() {
|
||||
return Err(SDLanError::IOError(format!("{} does not exists", RESOLV_FILE_BACKUP)));
|
||||
}
|
||||
|
||||
// 如果目标是符号链接,先删除链接再复制(避免写入到链接指向位置)
|
||||
if dst.is_symlink() {
|
||||
fs::remove_file(dst)?;
|
||||
} else if dst.exists() {
|
||||
fs::remove_file(dst)?;
|
||||
}
|
||||
|
||||
fs::copy(src, dst)?;
|
||||
|
||||
// 保留备份文件的权限
|
||||
let metadata = fs::metadata(src)?;
|
||||
let permissions = fs::Permissions::from_mode(metadata.mode());
|
||||
fs::set_permissions(dst, permissions)?;
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::chown;
|
||||
let uid = metadata.uid();
|
||||
let gid = metadata.gid();
|
||||
chown(dst, Some(uid), Some(gid))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user