2016-02-05 15:58:32 +00:00
// VpnCloud - Peer-to-Peer VPN
2021-02-08 09:11:20 +00:00
// Copyright (C) 2015-2021 Dennis Schwerdel
2016-02-05 15:58:32 +00:00
// This software is licensed under GPL-3 or newer (see LICENSE.md)
2015-11-19 15:34:20 +00:00
#[ macro_use ] extern crate log ;
2021-01-26 17:30:31 +00:00
#[ macro_use ] extern crate serde ;
2016-06-26 17:18:38 +00:00
2019-02-21 15:57:25 +00:00
#[ cfg(test) ] extern crate tempfile ;
2015-11-19 15:34:20 +00:00
2019-12-04 08:32:35 +00:00
#[ macro_use ]
pub mod util ;
#[ cfg(test) ]
#[ macro_use ]
mod tests ;
pub mod beacon ;
pub mod cloud ;
pub mod config ;
2016-06-27 13:43:30 +00:00
pub mod crypto ;
2019-12-04 08:32:35 +00:00
pub mod device ;
2020-09-24 17:48:13 +00:00
pub mod error ;
pub mod messages ;
2019-12-04 08:32:35 +00:00
pub mod net ;
2020-10-29 21:12:33 +00:00
pub mod oldconfig ;
2020-09-24 17:48:13 +00:00
pub mod payload ;
2016-06-30 08:05:37 +00:00
pub mod poll ;
2016-08-10 09:34:13 +00:00
pub mod port_forwarding ;
2020-09-24 17:48:13 +00:00
pub mod table ;
2019-01-09 16:45:12 +00:00
pub mod traffic ;
2019-12-04 08:32:35 +00:00
pub mod types ;
2021-02-04 20:42:38 +00:00
#[ cfg(feature = " websocket " ) ] pub mod wsproxy ;
2015-11-19 15:34:20 +00:00
2020-05-29 06:37:29 +00:00
use structopt ::StructOpt ;
2015-11-19 15:34:20 +00:00
2019-12-04 08:32:35 +00:00
use std ::{
2019-12-06 08:55:24 +00:00
fs ::{ self , File , Permissions } ,
2019-12-04 08:32:35 +00:00
io ::{ self , Write } ,
2020-09-24 17:48:13 +00:00
net ::{ Ipv4Addr , UdpSocket } ,
2019-12-06 08:55:24 +00:00
os ::unix ::fs ::PermissionsExt ,
2019-12-04 08:32:35 +00:00
path ::Path ,
2020-12-20 00:40:32 +00:00
process ,
2019-12-04 08:32:35 +00:00
str ::FromStr ,
2020-06-03 13:49:06 +00:00
sync ::Mutex ,
thread
2019-12-04 08:32:35 +00:00
} ;
2015-11-20 17:40:23 +00:00
2019-12-04 08:32:35 +00:00
use crate ::{
cloud ::GenericCloud ,
2021-02-05 17:27:48 +00:00
config ::{ Args , Command , Config , DEFAULT_PORT } ,
2020-09-24 17:48:13 +00:00
crypto ::Crypto ,
2019-12-04 08:32:35 +00:00
device ::{ Device , TunTapDevice , Type } ,
2020-12-20 00:40:32 +00:00
net ::Socket ,
2020-10-29 21:12:33 +00:00
oldconfig ::OldConfigFile ,
2020-09-24 17:48:13 +00:00
payload ::Protocol ,
2020-12-20 00:40:32 +00:00
util ::SystemTimeSource ,
2019-12-04 08:32:35 +00:00
} ;
2016-08-08 07:34:13 +00:00
2021-02-04 20:42:38 +00:00
#[ cfg(feature = " websocket " ) ]
use crate ::wsproxy ::ProxyConnection ;
2016-11-23 14:21:22 +00:00
struct DualLogger {
2020-10-25 20:48:43 +00:00
file : Option < Mutex < File > >
2016-11-23 14:21:22 +00:00
}
impl DualLogger {
pub fn new < P : AsRef < Path > > ( path : Option < P > ) -> Result < Self , io ::Error > {
if let Some ( path ) = path {
2019-12-19 15:08:51 +00:00
let path = path . as_ref ( ) ;
if path . exists ( ) {
fs ::remove_file ( path ) ?
}
2019-03-01 22:12:19 +00:00
let file = File ::create ( path ) ? ;
2020-10-25 20:48:43 +00:00
Ok ( DualLogger { file : Some ( Mutex ::new ( file ) ) } )
2016-11-23 14:21:22 +00:00
} else {
2020-10-25 20:48:43 +00:00
Ok ( DualLogger { file : None } )
2016-11-23 14:21:22 +00:00
}
}
}
2015-11-19 15:34:20 +00:00
2016-11-23 14:21:22 +00:00
impl log ::Log for DualLogger {
2016-06-11 14:08:57 +00:00
#[ inline ]
2019-01-01 23:35:14 +00:00
fn enabled ( & self , _metadata : & log ::Metadata ) -> bool {
2015-11-19 15:34:20 +00:00
true
}
2016-06-11 14:08:57 +00:00
#[ inline ]
2019-01-01 23:35:14 +00:00
fn log ( & self , record : & log ::Record ) {
2015-11-19 15:34:20 +00:00
if self . enabled ( record . metadata ( ) ) {
2016-11-23 14:21:22 +00:00
println! ( " {} - {} " , record . level ( ) , record . args ( ) ) ;
2020-10-25 20:48:43 +00:00
if let Some ( ref file ) = self . file {
let mut file = file . lock ( ) . expect ( " Lock poisoned " ) ;
2020-11-07 11:04:25 +00:00
let time = time ::OffsetDateTime ::now_local ( ) . format ( " %F %H:%M:%S " ) ;
2019-12-04 08:32:35 +00:00
writeln! ( file , " {} - {} - {} " , time , record . level ( ) , record . args ( ) )
. expect ( " Failed to write to logfile " ) ;
2016-11-23 14:21:22 +00:00
}
2015-11-19 15:34:20 +00:00
}
}
2019-01-01 23:35:14 +00:00
#[ inline ]
fn flush ( & self ) {
2020-10-25 20:48:43 +00:00
if let Some ( ref file ) = self . file {
let mut file = file . lock ( ) . expect ( " Lock poisoned " ) ;
2019-01-01 23:35:14 +00:00
try_fail! ( file . flush ( ) , " Logging error: {} " ) ;
}
}
2015-11-19 15:34:20 +00:00
}
2017-05-04 05:26:21 +00:00
fn run_script ( script : & str , ifname : & str ) {
2020-12-20 00:40:32 +00:00
let mut cmd = process ::Command ::new ( " sh " ) ;
2015-11-23 18:06:25 +00:00
cmd . arg ( " -c " ) . arg ( & script ) . env ( " IFNAME " , ifname ) ;
debug! ( " Running script: {:?} " , cmd ) ;
match cmd . status ( ) {
2019-12-04 08:32:35 +00:00
Ok ( status ) = > {
if ! status . success ( ) {
error! ( " Script returned with error: {:?} " , status . code ( ) )
}
}
2015-11-23 18:06:25 +00:00
Err ( e ) = > error! ( " Failed to execute script {:?}: {} " , script , e )
}
2015-11-19 15:34:20 +00:00
}
2020-09-24 17:48:13 +00:00
fn parse_ip_netmask ( addr : & str ) -> Result < ( Ipv4Addr , Ipv4Addr ) , String > {
let ( ip_str , len_str ) = match addr . find ( '/' ) {
Some ( pos ) = > ( & addr [ .. pos ] , & addr [ pos + 1 .. ] ) ,
None = > ( addr , " 24 " )
} ;
let prefix_len = u8 ::from_str ( len_str ) . map_err ( | _ | format! ( " Invalid prefix length: {} " , len_str ) ) ? ;
if prefix_len > 32 {
return Err ( format! ( " Invalid prefix length: {} " , prefix_len ) )
}
let ip = Ipv4Addr ::from_str ( ip_str ) . map_err ( | _ | format! ( " Invalid ip address: {} " , ip_str ) ) ? ;
let netmask = Ipv4Addr ::from ( u32 ::max_value ( ) . checked_shl ( 32 - prefix_len as u32 ) . unwrap ( ) ) ;
Ok ( ( ip , netmask ) )
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
fn setup_device ( config : & Config ) -> TunTapDevice {
let device = try_fail! (
TunTapDevice ::new ( & config . device_name , config . device_type , config . device_path . as_ref ( ) . map ( | s | s as & str ) ) ,
" Failed to open virtual {} interface {}: {} " ,
config . device_type ,
config . device_name
) ;
info! ( " Opened device {} " , device . ifname ( ) ) ;
2021-01-24 18:24:40 +00:00
config . call_hook ( " device_setup " , vec! [ ( " IFNAME " , device . ifname ( ) ) ] , true ) ;
2020-09-24 17:48:13 +00:00
if let Err ( err ) = device . set_mtu ( None ) {
error! ( " Error setting optimal MTU on {}: {} " , device . ifname ( ) , err ) ;
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
if let Some ( ip ) = & config . ip {
let ( ip , netmask ) = try_fail! ( parse_ip_netmask ( ip ) , " Invalid ip address given: {} " ) ;
info! ( " Configuring device with ip {}, netmask {} " , ip , netmask ) ;
try_fail! ( device . configure ( ip , netmask ) , " Failed to configure device: {} " ) ;
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
if let Some ( script ) = & config . ifup {
run_script ( script , device . ifname ( ) ) ;
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
if config . fix_rp_filter {
try_fail! ( device . fix_rp_filter ( ) , " Failed to change rp_filter settings: {} " ) ;
2017-05-04 06:22:24 +00:00
}
2020-09-24 17:48:13 +00:00
if let Ok ( val ) = device . get_rp_filter ( ) {
if val ! = 1 {
2020-10-31 09:12:19 +00:00
warn! ( " Your networking configuration might be affected by a vulnerability (https://vpncloud.ddswd.de/docs/security/cve-2019-14899/), please change your rp_filter setting to 1 (currently {}). " , val ) ;
2017-05-04 06:22:24 +00:00
}
}
2021-01-24 18:24:40 +00:00
config . call_hook ( " device_configured " , vec! [ ( " IFNAME " , device . ifname ( ) ) ] , true ) ;
2020-09-24 17:48:13 +00:00
device
2017-05-04 06:22:24 +00:00
}
2019-12-20 12:54:58 +00:00
#[ allow(clippy::cognitive_complexity) ]
2020-12-20 00:40:32 +00:00
fn run < P : Protocol , S : Socket > ( config : Config , socket : S ) {
2020-09-24 17:48:13 +00:00
let device = setup_device ( & config ) ;
2020-12-20 00:40:32 +00:00
let port_forwarding = if config . port_forwarding { socket . create_port_forwarding ( ) } else { None } ;
2019-12-06 08:55:24 +00:00
let stats_file = match config . stats_file {
None = > None ,
Some ( ref name ) = > {
2019-12-19 15:08:51 +00:00
let path = Path ::new ( name ) ;
if path . exists ( ) {
try_fail! ( fs ::remove_file ( path ) , " Failed to remove file {}: {} " , name ) ;
}
2019-12-06 08:55:24 +00:00
let file = try_fail! ( File ::create ( name ) , " Failed to create stats file: {} " ) ;
try_fail! (
fs ::set_permissions ( name , Permissions ::from_mode ( 0o644 ) ) ,
" Failed to set permissions on stats file: {} "
) ;
Some ( file )
}
} ;
let mut cloud =
2020-12-20 00:40:32 +00:00
GenericCloud ::< TunTapDevice , P , S , SystemTimeSource > ::new ( & config , socket , device , port_forwarding , stats_file ) ;
2021-02-05 17:27:48 +00:00
for mut addr in config . peers {
if addr . find ( ':' ) . unwrap_or ( 0 ) < = addr . find ( ']' ) . unwrap_or ( 0 ) {
// : not present or only in IPv6 address
addr = format! ( " {} : {} " , addr , DEFAULT_PORT )
}
2016-05-11 08:54:00 +00:00
try_fail! ( cloud . connect ( & addr as & str ) , " Failed to send message to {}: {} " , & addr ) ;
cloud . add_reconnect_peer ( addr ) ;
2015-11-25 11:29:12 +00:00
}
2016-11-23 14:21:22 +00:00
if config . daemonize {
info! ( " Running process as daemon " ) ;
let mut daemonize = daemonize ::Daemonize ::new ( ) ;
if let Some ( user ) = config . user {
daemonize = daemonize . user ( & user as & str ) ;
}
if let Some ( group ) = config . group {
daemonize = daemonize . group ( & group as & str ) ;
}
if let Some ( pid_file ) = config . pid_file {
daemonize = daemonize . pid_file ( pid_file ) . chown_pid_file ( true ) ;
2020-06-03 13:49:06 +00:00
// Give child process some time to write PID file
daemonize = daemonize . exit_action ( | | thread ::sleep ( std ::time ::Duration ::from_millis ( 10 ) ) ) ;
2016-11-23 14:21:22 +00:00
}
try_fail! ( daemonize . start ( ) , " Failed to daemonize: {} " ) ;
2019-12-06 07:54:27 +00:00
} else if config . user . is_some ( ) | | config . group . is_some ( ) {
info! ( " Dropping privileges " ) ;
let mut pd = privdrop ::PrivDrop ::default ( ) ;
if let Some ( user ) = config . user {
pd = pd . user ( user ) ;
}
if let Some ( group ) = config . group {
pd = pd . group ( group ) ;
}
try_fail! ( pd . apply ( ) , " Failed to drop privileges: {} " ) ;
2016-11-23 14:21:22 +00:00
}
2015-11-25 11:29:12 +00:00
cloud . run ( ) ;
2016-08-08 14:30:22 +00:00
if let Some ( script ) = config . ifdown {
2017-05-04 05:26:21 +00:00
run_script ( & script , cloud . ifname ( ) ) ;
2015-11-25 11:29:12 +00:00
}
}
fn main ( ) {
2020-05-29 06:37:29 +00:00
let args : Args = Args ::from_args ( ) ;
if args . version {
2020-09-24 17:48:13 +00:00
println! ( " VpnCloud v {} " , env! ( " CARGO_PKG_VERSION " ) ) ;
return
2015-11-25 11:29:12 +00:00
}
2020-05-29 06:37:29 +00:00
let logger = try_fail! ( DualLogger ::new ( args . log_file . as_ref ( ) ) , " Failed to open logfile: {} " ) ;
2019-01-01 23:35:14 +00:00
log ::set_boxed_logger ( Box ::new ( logger ) ) . unwrap ( ) ;
2020-05-29 06:37:29 +00:00
assert! ( ! args . verbose | | ! args . quiet ) ;
log ::set_max_level ( if args . verbose {
2019-12-04 08:32:35 +00:00
log ::LevelFilter ::Debug
2020-05-29 06:37:29 +00:00
} else if args . quiet {
2019-12-04 08:32:35 +00:00
log ::LevelFilter ::Error
} else {
log ::LevelFilter ::Info
} ) ;
2020-12-20 00:40:32 +00:00
if let Some ( cmd ) = args . cmd {
match cmd {
2021-02-05 17:27:48 +00:00
Command ::GenKey { password } = > {
let ( privkey , pubkey ) = Crypto ::generate_keypair ( password . as_deref ( ) ) ;
2020-12-20 00:40:32 +00:00
println! ( " Private key: {} \n Public key: {} \n " , privkey , pubkey ) ;
println! (
" Attention: Keep the private key secret and use only the public key on other nodes to establish trust. "
) ;
}
Command ::MigrateConfig { config_file } = > {
info! ( " Trying to convert from old config format " ) ;
let f = try_fail! ( File ::open ( & config_file ) , " Failed to open config file: {:?} " ) ;
let config_file_old : OldConfigFile =
try_fail! ( serde_yaml ::from_reader ( f ) , " Config file not valid for version 1: {:?} " ) ;
let new_config = config_file_old . convert ( ) ;
info! ( " Successfully converted from old format " ) ;
info! ( " Renaming original file to {}.orig " , config_file ) ;
try_fail! (
fs ::rename ( & config_file , format! ( " {} .orig " , config_file ) ) ,
" Failed to rename original file: {:?} "
) ;
info! ( " Writing new config back into {} " , config_file ) ;
let f = try_fail! ( File ::create ( & config_file ) , " Failed to open config file: {:?} " ) ;
try_fail! (
fs ::set_permissions ( & config_file , fs ::Permissions ::from_mode ( 0o600 ) ) ,
" Failed to set permissions on file: {:?} "
) ;
try_fail! ( serde_yaml ::to_writer ( f , & new_config ) , " Failed to write converted config: {:?} " ) ;
}
2021-02-03 21:44:31 +00:00
Command ::Completion { shell } = > {
Args ::clap ( ) . gen_completions_to ( env! ( " CARGO_PKG_NAME " ) , shell , & mut io ::stdout ( ) ) ;
return
}
2021-02-04 20:42:38 +00:00
#[ cfg(feature = " websocket " ) ]
2021-02-04 20:19:05 +00:00
Command ::WsProxy { listen } = > {
try_fail! ( wsproxy ::run_proxy ( & listen ) , " Failed to run websocket proxy: {:?} " ) ;
2020-12-20 00:40:32 +00:00
}
}
2020-10-29 21:12:33 +00:00
return
}
2016-08-08 14:30:22 +00:00
let mut config = Config ::default ( ) ;
2020-05-29 06:37:29 +00:00
if let Some ( ref file ) = args . config {
2016-08-08 14:30:22 +00:00
info! ( " Reading config file '{}' " , file ) ;
2017-07-22 14:34:20 +00:00
let f = try_fail! ( File ::open ( file ) , " Failed to open config file: {:?} " ) ;
2020-10-29 21:12:33 +00:00
let config_file = match serde_yaml ::from_reader ( f ) {
Ok ( config ) = > config ,
Err ( err ) = > {
error! ( " Failed to read config file: {} " , err ) ;
info! ( " Trying to convert from old config format " ) ;
let f = try_fail! ( File ::open ( file ) , " Failed to open config file: {:?} " ) ;
let config_file_old : OldConfigFile =
try_fail! ( serde_yaml ::from_reader ( f ) , " Config file is neither version 2 nor version 1: {:?} " ) ;
let new_config = config_file_old . convert ( ) ;
info! ( " Successfully converted from old format, please migrate your config using migrate-config " ) ;
new_config
}
} ;
2016-08-08 14:30:22 +00:00
config . merge_file ( config_file )
}
config . merge_args ( args ) ;
debug! ( " Config: {:?} " , config ) ;
2020-12-20 00:40:32 +00:00
if config . crypto . password . is_none ( ) & & config . crypto . private_key . is_none ( ) {
error! ( " Either password or private key must be set in config or given as parameter " ) ;
2021-02-03 21:44:31 +00:00
return
2020-12-20 00:40:32 +00:00
}
2021-02-04 20:42:38 +00:00
#[ cfg(feature = " websocket " ) ]
2020-12-20 12:28:01 +00:00
if config . listen . starts_with ( " ws:// " ) {
2020-12-20 00:40:32 +00:00
let socket = try_fail! ( ProxyConnection ::listen ( & config . listen ) , " Failed to open socket {}: {} " , config . listen ) ;
match config . device_type {
Type ::Tap = > run ::< payload ::Frame , _ > ( config , socket ) ,
2021-02-03 21:44:31 +00:00
Type ::Tun = > run ::< payload ::Packet , _ > ( config , socket )
2020-12-20 00:40:32 +00:00
}
2021-02-04 20:42:38 +00:00
return
}
let socket = try_fail! ( UdpSocket ::listen ( & config . listen ) , " Failed to open socket {}: {} " , config . listen ) ;
match config . device_type {
Type ::Tap = > run ::< payload ::Frame , _ > ( config , socket ) ,
Type ::Tun = > run ::< payload ::Packet , _ > ( config , socket )
2015-11-25 11:29:12 +00:00
}
2015-11-22 19:02:02 +00:00
}