Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only route local dns to active sessions #60

Merged
merged 3 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion linkup-cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "linkup-cli"
version = "0.2.7"
version = "0.2.8"
edition = "2021"

[[bin]]
Expand Down
83 changes: 57 additions & 26 deletions linkup-cli/src/local_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,46 @@ use thiserror::Error;

use linkup::{HeaderMap as LinkupHeaderMap, HeaderName as LinkupHeaderName, *};

use crate::LINKUP_LOCALSERVER_PORT;
use crate::{
linkup_file_path,
services::dnsmasq::{reload_dnsmasq_conf, write_dnsmaq_conf},
LINKUP_LOCALDNS_INSTALL, LINKUP_LOCALSERVER_PORT,
};

#[derive(Error, Debug)]
pub enum ProxyError {
#[error("reqwest proxy error {0}")]
ReqwestProxyError(String),
}

#[actix_web::main]
pub async fn local_linkup_main() -> io::Result<()> {
env_logger::Builder::new()
.filter(None, log::LevelFilter::Info)
.init();

let string_store = web::Data::new(MemoryStringStore::new());

println!("Starting local server on port {}", LINKUP_LOCALSERVER_PORT);
HttpServer::new(move || {
App::new()
.app_data(string_store.clone()) // Add shared state
.app_data(web::PayloadConfig::new(100 * 1024 * 1024)) // 100 MB payload limit
.wrap(middleware::Logger::default()) // Enable logger
.route("/linkup", web::post().to(linkup_config_handler))
.route("/linkup-check", web::route().to(always_ok))
.service(
web::resource("{path:.*}")
.guard(guard::Header("upgrade", "websocket"))
.to(linkup_ws_request_handler),
)
.default_service(web::route().to(linkup_request_handler))
})
.bind(("127.0.0.1", LINKUP_LOCALSERVER_PORT))?
.run()
.await
}

async fn linkup_config_handler(
string_store: web::Data<MemoryStringStore>,
req_body: web::Bytes,
Expand All @@ -33,11 +65,18 @@ async fn linkup_config_handler(
match update_session_req_from_json(input_json_conf) {
Ok((desired_name, server_conf)) => {
let sessions = SessionAllocator::new(string_store.as_ref());
let server_domains = server_conf.domain_selection_order.clone();
let session_name = sessions
.store_session(server_conf, NameKind::Animal, desired_name)
.await;
match session_name {
Ok(session_name) => HttpResponse::Ok().body(session_name),
Ok(session_name) => {
if linkup_file_path(LINKUP_LOCALDNS_INSTALL).exists() {
return add_local_dns_domain(server_domains, session_name);
}

HttpResponse::Ok().body(session_name)
}
Err(e) => HttpResponse::InternalServerError()
.append_header(ContentType::plaintext())
.body(format!("Failed to store server config: {}", e)),
Expand Down Expand Up @@ -258,30 +297,22 @@ fn no_redirect_client() -> reqwest::Client {
.unwrap()
}

#[actix_web::main]
pub async fn local_linkup_main() -> io::Result<()> {
env_logger::Builder::new()
.filter(None, log::LevelFilter::Info)
.init();
fn add_local_dns_domain(session_domains: Vec<String>, session_name: String) -> HttpResponse {
let dnsmasq_conf_domains = session_domains
.iter()
.map(|d| format!("{}.{}", session_name, d))
.collect();

let string_store = web::Data::new(MemoryStringStore::new());
if let Err(e) = write_dnsmaq_conf(Some(dnsmasq_conf_domains)) {
return HttpResponse::InternalServerError()
.append_header(ContentType::plaintext())
.body(format!("Failed to write dnsmasq config: {}", e));
};
if let Err(e) = reload_dnsmasq_conf() {
return HttpResponse::InternalServerError()
.append_header(ContentType::plaintext())
.body(format!("Failed to restart dnsmasq: {}", e));
};

println!("Starting local server on port {}", LINKUP_LOCALSERVER_PORT);
HttpServer::new(move || {
App::new()
.app_data(string_store.clone()) // Add shared state
.app_data(web::PayloadConfig::new(100 * 1024 * 1024)) // 100 MB payload limit
.wrap(middleware::Logger::default()) // Enable logger
.route("/linkup", web::post().to(linkup_config_handler))
.route("/linkup-check", web::route().to(always_ok))
.service(
web::resource("{path:.*}")
.guard(guard::Header("upgrade", "websocket"))
.to(linkup_ws_request_handler),
)
.default_service(web::route().to(linkup_request_handler))
})
.bind(("127.0.0.1", LINKUP_LOCALSERVER_PORT))?
.run()
.await
HttpResponse::Ok().body(session_name)
}
2 changes: 2 additions & 0 deletions linkup-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ pub enum CliError {
LocalDNSUninstall(String),
#[error("failed to write file: {0}")]
WriteFile(String),
#[error("failed to reboot dnsmasq: {0}")]
RebootDNSMasq(String),
}

#[derive(Parser)]
Expand Down
72 changes: 52 additions & 20 deletions linkup-cli/src/services/dnsmasq.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use std::{
fs,
path::Path,
process::{Command, Stdio},
};

use nix::sys::signal::Signal;
use nix::sys::signal::{kill, Signal};
use nix::unistd::Pid;
use std::fmt::Write;

use crate::{linkup_dir_path, linkup_file_path, stop::stop_pid_file, CliError, Result};

Expand All @@ -14,18 +15,7 @@ const LOG_FILE: &str = "dnsmasq-log";
const PID_FILE: &str = "dnsmasq-pid";

pub fn start() -> Result<()> {
let conf_file_path = linkup_file_path(CONF_FILE);
let logfile_path = linkup_file_path(LOG_FILE);
let pidfile_path = linkup_file_path(PID_FILE);

if fs::write(&logfile_path, "").is_err() {
return Err(CliError::WriteFile(format!(
"Failed to write dnsmasq log file at {}",
logfile_path.display()
)));
}

write_conf_file(&conf_file_path, &logfile_path, &pidfile_path)?;
let conf_file_path = write_dnsmaq_conf(None)?;

Command::new("dnsmasq")
.current_dir(linkup_dir_path())
Expand All @@ -45,14 +35,30 @@ pub fn stop() {
let _ = stop_pid_file(&linkup_file_path(PID_FILE), Signal::SIGTERM);
}

fn write_conf_file(conf_file_path: &Path, logfile_path: &Path, pidfile_path: &Path) -> Result<()> {
pub fn write_dnsmaq_conf(local_domains: Option<Vec<String>>) -> Result<String> {
let conf_file_path = linkup_file_path(CONF_FILE);
let logfile_path = linkup_file_path(LOG_FILE);
let pidfile_path = linkup_file_path(PID_FILE);
let mut local_domains_template = String::new();
if let Some(local_domains) = local_domains {
local_domains_template = local_domains.iter().fold(String::new(), |mut acc, d| {
let _ = write!(acc, "address=/{}/127.0.0.1\naddress=/{}/::1\n", d, d);
acc
});
}

let dnsmasq_template = format!(
"
address=/#/127.0.0.1
port={}
log-facility={}
pid-file={}
# Set of domains that should be routed locally
{}

# Other dnsmasq config options
server=1.1.1.1
port={}
log-facility={}
pid-file={}
",
local_domains_template,
PORT,
logfile_path.display(),
pidfile_path.display(),
Expand All @@ -61,9 +67,35 @@ fn write_conf_file(conf_file_path: &Path, logfile_path: &Path, pidfile_path: &Pa
if fs::write(conf_file_path, dnsmasq_template).is_err() {
return Err(CliError::WriteFile(format!(
"Failed to write dnsmasq config at {}",
conf_file_path.display()
linkup_file_path(CONF_FILE).display()
)));
}

Ok(linkup_file_path(CONF_FILE)
.to_str()
.expect("const path known to be valid")
.to_string())
}

pub fn reload_dnsmasq_conf() -> Result<()> {
let pidfile_path = linkup_file_path(PID_FILE);
let pid_str = fs::read_to_string(pidfile_path).map_err(|e| {
CliError::RebootDNSMasq(format!(
"Failed to read PID file at {}: {}",
linkup_file_path(PID_FILE).display(),
e
))
})?;

// Parse the PID from the file content
let pid = pid_str
.trim()
.parse::<i32>()
.map_err(|e| CliError::RebootDNSMasq(format!("Invalid PID value: {}", e)))?;

// Send SIGHUP signal to the dnsmasq process
kill(Pid::from_raw(pid), Signal::SIGHUP)
.map_err(|e| CliError::RebootDNSMasq(format!("Failed to send SIGHUP to dnsmasq: {}", e)))?;

Ok(())
}