aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
blob: 92b74d9a8ba66a6531485fa01c9e5e383301618c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#![feature(test)]

#[macro_use] extern crate failure;
#[macro_use] extern crate structopt_derive;
#[macro_use] extern crate log;

extern crate chrono;
extern crate colored;
extern crate daemonize;
extern crate fern;
extern crate nix;
extern crate structopt;
extern crate wireguard;

use colored::*;
use daemonize::Daemonize;
use failure::Error;
use fern::colors::{Color, ColoredLevelConfig};
use wireguard::interface::Interface;
use structopt::StructOpt;

use std::{env, process};

#[derive(StructOpt, Debug)]
#[structopt(name = "wgrs", about = "WireGuard - a network tunnel")]
struct Opt {
    /// A flag, true if used in the command line.
    #[structopt(short = "d", long = "debug", help = "Activate debug mode")]
    debug: bool,

    /// An argument of type float, with a default value.
    #[structopt(short = "f", long = "foreground", help = "Run in the foreground")]
    foreground: bool,

    /// Needed parameter, the first on the command line.
    #[structopt(help = "WireGuard interface name")]
    interface: String,

    /// An optional parameter, will be `None` if not present on the
    /// command line.
    #[structopt(help = "Output file, stdout if not present")]
    output: Option<String>,
}

fn warning() {
    let should_quit = match env::var("WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD") {
        Ok(ref val) if val == "1" => false,
        _                         => true
    };

    println!("\nWARNING WARNING WARNING WARNING WARNING WARNING WARNING");
    println!("W                                                     G");
    println!("W   This is alpha software. It will very likely not   G");
    println!("W   do what it is supposed to do, and things may go   G");
    println!("W   horribly wrong. You have been warned. Proceed     G");
    println!("W   at your own risk.                                 G");
    if cfg!(target_os = "linux") {
        println!("W                                                     G");
        println!("W   Furthermore, you are running this software on a   G");
        println!("W   Linux kernel, which is probably unnecessary and   G");
        println!("W   foolish. This is because the Linux kernel has     G");
        println!("W   built-in first class support for WireGuard, and   G");
        println!("W   this support is much more refined than this       G");
        println!("W   program. For more information on installing the   G");
        println!("W   kernel module, please visit:                      G");
        println!("W           https://www.wireguard.com/install         G");
    }
    if should_quit {
        println!("W                                                     G");
        println!("W   If you still want to use this program, against    G");
        println!("W   the sage advice here, please first export this    G");
        println!("W   environment variable:                             G");
        println!("W   WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD=1    G");
    }

    println!("W                                                     G");
    println!("WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n");

    if should_quit {
        process::exit(1);
    }
}

fn main() {
    let opt = Opt::from_args();

    warning();

    let interface = opt.interface.clone();
    let colors = ColoredLevelConfig::new()
        .debug(Color::Magenta)
        .info(Color::BrightBlue)
        .warn(Color::BrightYellow)
        .error(Color::BrightRed);
    fern::Dispatch::new()
        .format(move |out, message, record| {
            let pad = record.level() == log::Level::Warn || record.level() == log::Level::Info;
            out.finish(format_args!(
                "{} {}  {}{}  {}",
                chrono::Local::now().format("%H:%M:%S%.3f"),
                interface,
                colors.color(record.level()),
                if pad { " " } else { "" },
                message,
            ))
        })
        .level(log::LevelFilter::Info)
        .level_for("wireguard", log::LevelFilter::Debug)
        .chain(std::io::stdout())
        .apply().unwrap();

    if !opt.foreground {
        if let Err(e) = daemonize() {
            println!("{}", format!("ERROR: {}", e.cause()).bold().red());
            process::exit(1);
        }
    }

    if let Err(e) = Interface::new(&opt.interface).start() {
        error!("failed to start interface: {}", e);
    }
}

fn daemonize() -> Result<(), Error> {
    if !nix::unistd::getuid().is_root() {
        bail!("This must be run as root to initialize the tunnel.");
    }

    debug!("Starting daemon.");
    let daemonize = Daemonize::new()
        .stream_redirect("/var/log/wireguard.log");

    daemonize.start()?;
    Ok(())
}