mirror of
https://github.com/tun2proxy/tun2proxy.git
synced 2025-05-14 14:02:45 +00:00
Improve setup error handling and describe the setup function in the README
This commit is contained in:
parent
bfface515d
commit
d0c24b9f6a
2 changed files with 93 additions and 44 deletions
10
README.md
10
README.md
|
@ -1,7 +1,5 @@
|
|||
# tun2proxy
|
||||
Tunnel TCP traffic through SOCKS or HTTP on Linux.
|
||||
|
||||
**Error handling incomplete and too restrictive.**
|
||||
A tunnel interface for HTTP and SOCKS proxies on Linux based on [smoltcp](https://github.com/smoltcp-rs/smoltcp).
|
||||
|
||||
## Build
|
||||
Clone the repository and `cd` into the project folder. Then run the following:
|
||||
|
@ -10,6 +8,12 @@ cargo build --release
|
|||
```
|
||||
|
||||
## Setup
|
||||
## Automated Setup
|
||||
Using `--setup auto`, you can have tun2proxy configure your system to automatically route all traffic through the
|
||||
specified proxy. This requires running the tool as root and will roughly perform the steps outlined in the section
|
||||
describing the manual setup, except that a bind mount is used to overlay the `/etc/resolv.conf` file.
|
||||
|
||||
## Manual Setup
|
||||
A standard setup, which would route all traffic from your system through the tunnel interface, could look as follows:
|
||||
```shell
|
||||
# The proxy type can be either SOCKS4, SOCKS5 or HTTP.
|
||||
|
|
127
src/setup.rs
127
src/setup.rs
|
@ -1,11 +1,11 @@
|
|||
use crate::error::Error;
|
||||
use smoltcp::wire::IpCidr;
|
||||
use std::ffi::CString;
|
||||
use std::ffi::{CString, OsStr};
|
||||
use std::io::{BufRead, Write};
|
||||
use std::mem;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use std::os::fd::FromRawFd;
|
||||
use std::process::Command;
|
||||
use std::process::{Command, Output};
|
||||
use std::ptr::null;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -26,6 +26,43 @@ pub fn get_default_cidrs() -> [IpCidr; 4] {
|
|||
]
|
||||
}
|
||||
|
||||
fn run_iproute<I, S>(args: I, error: &str, require_success: bool) -> Result<Output, Error>
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: AsRef<OsStr>,
|
||||
{
|
||||
let mut command = Command::new("");
|
||||
for (i, arg) in args.into_iter().enumerate() {
|
||||
if i == 0 {
|
||||
command = Command::new(arg);
|
||||
} else {
|
||||
command.arg(arg);
|
||||
}
|
||||
}
|
||||
|
||||
let e = Error::from(error);
|
||||
let output = command.output().map_err(|_| e)?;
|
||||
if !require_success || output.status.success() {
|
||||
Ok(output)
|
||||
} else {
|
||||
let mut args: Vec<&str> = command.get_args().map(|x| x.to_str().unwrap()).collect();
|
||||
let program = command.get_program().to_str().unwrap();
|
||||
let mut cmdline = Vec::<&str>::new();
|
||||
cmdline.push(program);
|
||||
cmdline.append(&mut args);
|
||||
let command = cmdline.as_slice().join(" ");
|
||||
match String::from_utf8(output.stderr.clone()) {
|
||||
Ok(output) => Err(format!("Command `{}` failed: {}", command, output).into()),
|
||||
Err(_) => Err(format!(
|
||||
"Command `{:?}` failed with exit code {}",
|
||||
command,
|
||||
output.status.code().unwrap()
|
||||
)
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Setup {
|
||||
pub fn new(
|
||||
tun: impl Into<String>,
|
||||
|
@ -43,16 +80,12 @@ impl Setup {
|
|||
|
||||
fn clone_default_route(&mut self) -> Result<(), Error> {
|
||||
let route_show_args = if self.proxy_addr.is_ipv6() {
|
||||
Vec::from(["-6", "route", "show"])
|
||||
["ip", "-6", "route", "show"]
|
||||
} else {
|
||||
Vec::from(["-4", "route", "show"])
|
||||
["ip", "-4", "route", "show"]
|
||||
};
|
||||
|
||||
let e = Error::from("failed to get routing table");
|
||||
let routes = Command::new("ip")
|
||||
.args(route_show_args.as_slice())
|
||||
.output()
|
||||
.map_err(|_| e)?;
|
||||
let routes = run_iproute(route_show_args, "failed to get routing table", true)?;
|
||||
|
||||
// Equivalent of `ip route | grep '^default' | cut -d ' ' -f 2-`
|
||||
let mut default_route_args = Vec::<String>::new();
|
||||
|
@ -72,14 +105,14 @@ impl Setup {
|
|||
}
|
||||
}
|
||||
|
||||
let e = Error::from("failed to clone default route for proxy");
|
||||
let mut proxy_route = vec!["route".to_string(), "add".to_string()];
|
||||
let mut proxy_route = vec!["ip".into(), "route".into(), "add".into()];
|
||||
proxy_route.push(self.proxy_addr.to_string());
|
||||
proxy_route.extend(default_route_args.clone());
|
||||
Command::new("ip")
|
||||
.args(proxy_route)
|
||||
.output()
|
||||
.map_err(|_| e)?;
|
||||
proxy_route.extend(default_route_args.into_iter());
|
||||
run_iproute(
|
||||
proxy_route,
|
||||
"failed to clone default route for proxy",
|
||||
false,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -115,31 +148,35 @@ impl Setup {
|
|||
|
||||
fn add_tunnel_routes(&self) -> Result<(), Error> {
|
||||
for route in &self.routes {
|
||||
let e = Error::from(format!(
|
||||
"failed to set up routing of {} through {}",
|
||||
route, self.tun
|
||||
));
|
||||
Command::new("ip")
|
||||
.args([
|
||||
run_iproute(
|
||||
[
|
||||
"ip",
|
||||
"route",
|
||||
"add",
|
||||
route.to_string().as_str(),
|
||||
"dev",
|
||||
self.tun.as_str(),
|
||||
])
|
||||
.output()
|
||||
.map_err(|_| e)?;
|
||||
],
|
||||
"failed to add route",
|
||||
true,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn shutdown(&self) {
|
||||
Self::shutdown_with_args(&self.tun);
|
||||
if !self.set_up {
|
||||
return;
|
||||
}
|
||||
Self::shutdown_with_args(&self.tun, self.proxy_addr);
|
||||
}
|
||||
|
||||
fn shutdown_with_args(tun_name: &str) {
|
||||
fn shutdown_with_args(tun_name: &str, proxy_ip: IpAddr) {
|
||||
log::info!("Restoring network configuration");
|
||||
let _ = Command::new("ip").args(["link", "del", tun_name]).output();
|
||||
let _ = Command::new("ip")
|
||||
.args(["route", "del", proxy_ip.to_string().as_str()])
|
||||
.output();
|
||||
unsafe {
|
||||
let umount_path = CString::new("/etc/resolv.conf").unwrap();
|
||||
libc::umount(umount_path.as_ptr());
|
||||
|
@ -147,32 +184,40 @@ impl Setup {
|
|||
}
|
||||
|
||||
pub fn setup(&mut self) -> Result<(), Error> {
|
||||
self.set_up = true;
|
||||
|
||||
unsafe {
|
||||
if libc::getuid() != 0 {
|
||||
return Err("Automatic setup requires root privileges".into());
|
||||
}
|
||||
}
|
||||
|
||||
run_iproute(
|
||||
[
|
||||
"ip",
|
||||
"tuntap",
|
||||
"add",
|
||||
"name",
|
||||
self.tun.as_str(),
|
||||
"mode",
|
||||
"tun",
|
||||
],
|
||||
"failed to create tunnel device",
|
||||
true,
|
||||
)?;
|
||||
|
||||
self.set_up = true;
|
||||
let tun_name = self.tun.clone();
|
||||
let proxy_ip = self.proxy_addr;
|
||||
// TODO: This is not optimal.
|
||||
ctrlc::set_handler(move || {
|
||||
Self::shutdown_with_args(&tun_name);
|
||||
Self::shutdown_with_args(&tun_name, proxy_ip);
|
||||
std::process::exit(0);
|
||||
})?;
|
||||
|
||||
let e = Error::from("failed to create tunnel device");
|
||||
Command::new("ip")
|
||||
.args(["tuntap", "add", "name", self.tun.as_str(), "mode", "tun"])
|
||||
.output()
|
||||
.map_err(|_| e)?;
|
||||
|
||||
let e = Error::from("failed to bring up tunnel device");
|
||||
Command::new("ip")
|
||||
.args(["link", "set", self.tun.as_str(), "up"])
|
||||
.output()
|
||||
.map_err(|_| e)?;
|
||||
run_iproute(
|
||||
["ip", "link", "set", self.tun.as_str(), "up"],
|
||||
"failed to bring up tunnel device",
|
||||
true,
|
||||
)?;
|
||||
|
||||
self.clone_default_route()?;
|
||||
Self::setup_resolv_conf()?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue