Skip to content

Commit

Permalink
feat(tailscale): move dns utils to their own module
Browse files Browse the repository at this point in the history
  • Loading branch information
NotAShelf committed Mar 18, 2024
1 parent 3686059 commit b72f72c
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 122 deletions.
9 changes: 9 additions & 0 deletions src/tailscale/dns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::tailscale::utils::{sanitize_hostname, trim_suffix, Machine, PeerKind};

pub fn dns_or_quote_hostname(m: &mut Machine, dns_suffix: &str) {
let base_name = trim_suffix(&m.dns_name, dns_suffix);
m.display_name = match base_name {
n if n.is_empty() => PeerKind::DNSName(sanitize_hostname(m.hostname.as_str())),
base => PeerKind::HostName(base),
}
}
2 changes: 2 additions & 0 deletions src/tailscale/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
pub mod dns;
pub mod peer;
pub mod status;
pub mod utils;
47 changes: 47 additions & 0 deletions src/tailscale/peer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use crate::clipboard::{copy_to_clipboard, get_from_clipboard};
use log::{error, info};
use notify_rust::Notification;

pub fn check_peer_ip(peer_ip: &str) {
if peer_ip.is_empty() {
error!("No peer IP.")
} else {
info!("Peer IP: {}", peer_ip);
}
}

pub fn copy_peer_ip(peer_ip: &str, notif_title: &str) {
check_peer_ip(peer_ip);

match copy_to_clipboard(peer_ip) {
Ok(_) => {
// Get IP from clipboard to verify
match get_from_clipboard() {
Ok(clip_ip) => {
// log success
info!("Copied IP address {} to the Clipboard", clip_ip);

// send a notification through dbus
let body = format!("Copied IP address {} to the Clipboard", clip_ip);
let _result = Notification::new()
.summary(notif_title)
.body(&body)
.icon("info")
.show();
}

Err(e) => {
let message = "Failed to get IP from clipboard";
error!("{}: {}", message, e);

let _result = Notification::new()
.summary(notif_title)
.body(&message)
.icon("error")
.show();
}
}
}
Err(e) => error!("Failed to copy IP to clipboard: {}", e),
}
}
50 changes: 47 additions & 3 deletions src/tailscale/status.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,58 @@
use crate::tailscale::utils;
use crate::tailscale::dns;
use crate::tailscale::utils::{Machine, User};
use crate::tray::Context;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, process::Command};
use which::which;

#[derive(Serialize, Deserialize, Debug)]
pub struct Status {
// TODO: mutex
#[serde(skip)]
pub tailscale_up: bool,
#[serde(rename(deserialize = "BackendState"))]
backend_state: String,
#[serde(rename(deserialize = "Self"))]
pub this_machine: Machine,
#[serde(rename(deserialize = "MagicDNSSuffix"))]
magic_dnssuffix: String,
#[serde(rename(deserialize = "Peer"))]
pub peers: HashMap<String, Machine>,
#[serde(rename(deserialize = "User"))]
user: HashMap<String, User>,
}

pub fn get_current_status() -> Context {
let status: utils::Status = utils::get_status().unwrap();

let status: Status = get_status().unwrap();

Context {
ip: status.this_machine.ips[0].clone(),
pkexec: which("pkexec").unwrap(),
status,
}
}

pub fn get_status_json() -> String {
let output = Command::new("tailscale")
.arg("status")
.arg("--json")
.output()
.expect("Fetch tailscale status fail.");
String::from_utf8(output.stdout).expect("Unable to convert status output string.")
}

pub fn get_status() -> Result<Status, serde_json::Error> {
let mut st: Status = serde_json::from_str(get_status_json().as_str())?;
let dnssuffix = st.magic_dnssuffix.to_owned();
st.tailscale_up = match st.backend_state.as_str() {
"Running" => true,
"Stopped" => false,
_ => false,
};

dns::dns_or_quote_hostname(&mut st.this_machine, &dnssuffix);
st.peers
.values_mut()
.for_each(|m: &mut Machine| dns::dns_or_quote_hostname(m, &dnssuffix));
Ok(st)
}
64 changes: 9 additions & 55 deletions src/tailscale/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fmt::{Display, Formatter},
process::Command,
};

#[derive(Debug)]
pub enum PeerKind {
DNSName(String),
HostName(String),
}

impl Default for PeerKind {
fn default() -> Self {
PeerKind::HostName("default".to_owned())
}
}

impl Display for PeerKind {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self {
Expand All @@ -29,14 +30,15 @@ pub struct Machine {
#[serde(skip)]
pub display_name: PeerKind,
#[serde(rename(deserialize = "DNSName"))]
dns_name: String,
pub dns_name: String,
#[serde(rename(deserialize = "HostName"))]
hostname: String,
pub hostname: String,
#[serde(rename(deserialize = "TailscaleIPs"))]
pub ips: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
struct User {
pub struct User {
#[serde(rename(deserialize = "ID"))]
id: u64,
#[serde(rename(deserialize = "LoginName"))]
Expand All @@ -48,64 +50,16 @@ struct User {
#[serde(rename(deserialize = "Roles"))]
roles: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug)]

pub struct Status {
// TODO: mutex
#[serde(skip)]
pub tailscale_up: bool,
#[serde(rename(deserialize = "BackendState"))]
backend_state: String,
#[serde(rename(deserialize = "Self"))]
pub this_machine: Machine,
#[serde(rename(deserialize = "MagicDNSSuffix"))]
magic_dnssuffix: String,
#[serde(rename(deserialize = "Peer"))]
pub peers: HashMap<String, Machine>,
#[serde(rename(deserialize = "User"))]
user: HashMap<String, User>,
}

pub fn get_status() -> Result<Status, serde_json::Error> {
let mut st: Status = serde_json::from_str(get_json().as_str())?;
let dnssuffix = st.magic_dnssuffix.to_owned();
st.tailscale_up = match st.backend_state.as_str() {
"Running" => true,
"Stopped" => false,
_ => false,
};
dns_or_quote_hostname(&mut st.this_machine, &dnssuffix);
st.peers
.values_mut()
.for_each(|m: &mut Machine| dns_or_quote_hostname(m, &dnssuffix));
Ok(st)
}

fn get_json() -> String {
let output = Command::new("tailscale")
.arg("status")
.arg("--json")
.output()
.expect("Fetch tailscale status fail.");
String::from_utf8(output.stdout).expect("Unable to convert status output string.")
}

fn dns_or_quote_hostname(m: &mut Machine, dns_suffix: &str) {
let base_name = trim_suffix(&m.dns_name, dns_suffix);
m.display_name = match base_name {
n if n.is_empty() => PeerKind::DNSName(sanitize_hostname(m.hostname.as_str())),
base => PeerKind::HostName(base),
}
}
fn has_suffix(name: &str, suffix: &str) -> bool {
pub fn has_suffix(name: &str, suffix: &str) -> bool {
let name = name.trim_end_matches('.');
let mut suffix = suffix.trim_end_matches('.');
suffix = suffix.trim_start_matches('.');
let name_base = name.trim_end_matches(suffix);
name_base.len() < name.len() && name_base.ends_with('.')
}

fn trim_suffix(name: &str, suffix: &str) -> String {
pub fn trim_suffix(name: &str, suffix: &str) -> String {
let mut new_name = name;
if has_suffix(name, suffix) {
new_name = new_name.trim_end_matches('.');
Expand All @@ -115,7 +69,7 @@ fn trim_suffix(name: &str, suffix: &str) -> String {
new_name.trim_end_matches('.').to_string()
}

fn sanitize_hostname(hostname: &str) -> String {
pub fn sanitize_hostname(hostname: &str) -> String {
const MAX_LABEL_LEN: usize = 63;
let mut sb = "".to_string();
let hostname = hostname
Expand Down
72 changes: 8 additions & 64 deletions src/tray.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use crate::clipboard::{copy_to_clipboard, get_from_clipboard};
use crate::pkexec::{get_pkexec_path, pkexec_found};
use crate::svg::utils::ResvgRenderer;
use crate::tailscale::peer::copy_peer_ip;
use crate::tailscale::status::{get_current_status, Status};
use crate::tailscale::utils::PeerKind;
use crate::tailscale::utils::{get_status, Status};

use ksni::{
self,
menu::{StandardItem, SubMenu},
Icon, MenuItem, ToolTip, Tray,
};
use log::{error, info};

use log::info;
use notify_rust::Notification;
use std::{
path::PathBuf,
Expand All @@ -31,29 +32,16 @@ pub struct SysTray {
impl SysTray {
pub fn new() -> Self {
SysTray {
ctx: Self::get_current_status(),
ctx: get_current_status(),
}
}

fn enabled(&self) -> bool {
self.ctx.status.tailscale_up
}

fn get_current_status() -> Context {
let status: Status = get_status().unwrap();
let pkexec_path = get_pkexec_path();
let ctx = Context {
ip: status.this_machine.ips[0].clone(),
pkexec: pkexec_path.clone(),
status,
};

assert_eq!(ctx.pkexec, pkexec_path);
ctx
}

fn update_status(&mut self) {
self.ctx = Self::get_current_status();
self.ctx = get_current_status();
}

fn do_service_link(&mut self, verb: &str) {
Expand Down Expand Up @@ -90,50 +78,6 @@ impl SysTray {
self.update_status();
}
}

fn check_peer_ip(peer_ip: &str) {
if peer_ip.is_empty() {
error!("No peer IP.")
} else {
info!("Peer IP: {}", peer_ip);
}
}

fn copy_peer_ip(peer_ip: &str, notif_title: &str) {
Self::check_peer_ip(peer_ip);

match copy_to_clipboard(peer_ip) {
Ok(_) => {
// Get IP from clipboard to verify
match get_from_clipboard() {
Ok(clip_ip) => {
// log success
info!("Copied IP address {} to the Clipboard", clip_ip);

// send a notification through dbus
let body = format!("Copied IP address {} to the Clipboard", clip_ip);
let _result = Notification::new()
.summary(notif_title)
.body(&body)
.icon("info")
.show();
}

Err(e) => {
let message = "Failed to get IP from clipboard";
error!("{}: {}", message, e);

let _result = Notification::new()
.summary(notif_title)
.body(&message)
.icon("error")
.show();
}
}
}
Err(e) => error!("Failed to copy IP to clipboard: {}", e),
}
}
}

impl Tray for SysTray {
Expand Down Expand Up @@ -188,7 +132,7 @@ impl Tray for SysTray {
let peer_title = title.to_owned();
let menu = MenuItem::Standard(StandardItem {
label: format!("{}\t({})", title, ip),
activate: Box::new(move |_: &mut Self| Self::copy_peer_ip(&peer_ip, &peer_title)),
activate: Box::new(move |_: &mut Self| copy_peer_ip(&peer_ip, &peer_title)),
..Default::default()
});
sub.push(menu);
Expand Down Expand Up @@ -218,7 +162,7 @@ impl Tray for SysTray {
"This device: {} ({})",
self.ctx.status.this_machine.display_name, self.ctx.ip
),
activate: Box::new(move |_| Self::copy_peer_ip(&my_ip, "This device")),
activate: Box::new(move |_| copy_peer_ip(&my_ip, "This device")),
..Default::default()
}
.into(),
Expand Down

0 comments on commit b72f72c

Please sign in to comment.