Small program that establishes and maintains SOCKS5-tunnel via ssh utility.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
3.2 KiB

use std::collections::HashMap;
use std::fs::read_to_string;
struct LineLexer {
linenum: usize,
content: Vec<String>,
current: String,
previous: String,
}
impl LineLexer {
pub fn new(data: String) -> Self {
Self {
linenum: 0,
content: data.lines().map(String::from).collect(),
current: String::new(),
previous: String::new(),
}
}
pub fn next(&mut self) -> Option<String> {
let line = self.content.get(self.linenum);
if line.is_some() {
self.previous = self.current.clone();
self.current = line.unwrap().trim().to_string();
self.linenum += 1;
}
line.map(|s| s.clone())
}
pub fn next_if_nonempty(&mut self) -> Option<String> {
let l = self.next();
if l.is_none() || l.as_ref().unwrap().len() > 0 {
l
} else {
None
}
}
pub fn next_nonempty(&mut self) -> Option<String> {
loop {
let l = self.next();
if l.is_none() {
return l;
}
if l.as_ref().unwrap().len() > 0 {
return l;
}
}
}
}
#[derive(Debug)]
pub struct TinySSHHost {
hostname: String,
entries: HashMap<String, String>,
}
impl TinySSHHost {
pub fn new(hostname: String) -> Self {
TinySSHHost {
hostname,
entries: HashMap::new(),
}
}
pub fn get(&self, k: &str) -> Option<&String> {
self.entries.get(&k.to_string())
}
pub fn can_be_proxy(&self) -> bool {
self.get("DynamicForward").is_some()
}
}
pub struct TinySSHConfig {
hosts: Vec<TinySSHHost>,
}
impl TinySSHConfig {
pub fn parse<S: Into<String>>(filename: S) -> Result<Self, String> {
let data = read_to_string(filename.into()).map_err(|e| e.to_string())?;
let mut tsc = Self { hosts: vec![] };
let mut lexer = LineLexer::new(data);
loop {
let line = lexer.next_nonempty();
if line.is_none() {
break;
}
let mut host = if lexer.current.starts_with("Host") {
match lexer.current.split_whitespace().nth(1) {
Some(h) => TinySSHHost::new(h.trim().to_string()),
None => return Err("No hostname found".to_string()),
}
} else {
return Err("Wrong section order".to_string());
};
loop {
let l = lexer.next_if_nonempty();
if l.is_none() {
break;
}
let l = l.unwrap();
let mut param: Vec<String> = l.trim().split(' ').map(String::from).collect();
if param.len() != 2 {
return Err(format!("Wrong param format: {}", l));
}
let v = param.pop().unwrap();
let k = param.pop().unwrap();
host.entries.insert(k, v);
}
tsc.hosts.push(host);
}
Ok(tsc)
}
pub fn get_host(&self, host: &str) -> Option<&TinySSHHost> {
self.hosts.iter().find(|h| h.hostname == host)
}
}