Changes to be committed:
modified: Cargo.lock modified: Cargo.toml modified: src/client.rs modified: src/main.rs modified: src/server.rs
This commit is contained in:
parent
8bf6d57ba8
commit
c8f1835e1a
87
Cargo.lock
generated
87
Cargo.lock
generated
@ -152,6 +152,12 @@ dependencies = [
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
@ -395,6 +401,32 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek"
|
||||
version = "4.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"curve25519-dalek-derive",
|
||||
"fiat-crypto",
|
||||
"rustc_version",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "curve25519-dalek-derive"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
@ -430,6 +462,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.30"
|
||||
@ -1214,6 +1252,15 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.34"
|
||||
@ -1235,6 +1282,7 @@ dependencies = [
|
||||
"aes-gcm",
|
||||
"aes-soft",
|
||||
"anyhow",
|
||||
"base64",
|
||||
"bincode",
|
||||
"block-modes",
|
||||
"block-padding",
|
||||
@ -1258,6 +1306,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tun",
|
||||
"tun2",
|
||||
"x25519-dalek",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1272,6 +1321,12 @@ version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.205"
|
||||
@ -1772,6 +1827,18 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x25519-dalek"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
|
||||
dependencies = [
|
||||
"curve25519-dalek",
|
||||
"rand_core",
|
||||
"serde",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
@ -1792,3 +1859,23 @@ dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
|
||||
dependencies = [
|
||||
"zeroize_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize_derive"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.72",
|
||||
]
|
||||
|
@ -14,7 +14,7 @@ tun = "0.6.1"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0.190"
|
||||
bincode = "1.3"
|
||||
rand = { version = "0.8.5", features = ["small_rng"] }
|
||||
rand = { version = "0.8.5", features = ["small_rng", "getrandom", "std_rng"] }
|
||||
anyhow = "1.0"
|
||||
ctrlc = "3.1"
|
||||
aes = "0.7"
|
||||
@ -33,3 +33,5 @@ pnet = "0.35.0"
|
||||
net-route = "0.4.4"
|
||||
hex = "0.4"
|
||||
serde_yaml = "0.9.34"
|
||||
x25519-dalek = { version = "2.0.1", features = ["getrandom", "static_secrets"] }
|
||||
base64 = "0.22.1"
|
||||
|
@ -11,7 +11,7 @@ use std::collections::HashMap;
|
||||
use std::process::Command;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
use crate::{UDPVpnHandshake, UDPVpnPacket, VpnPacket, ClientConfiguration};
|
||||
use crate::{UDPVpnHandshake, UDPVpnPacket, VpnPacket, ClientConfiguration, UDPSerializable};
|
||||
|
||||
fn configure_routes() {
|
||||
let ip_output = Command::new("ip")
|
||||
@ -111,7 +111,7 @@ pub async fn client_mode(client_config: ClientConfiguration) {
|
||||
}
|
||||
});
|
||||
|
||||
let handshake = UDPVpnHandshake{};
|
||||
let handshake = UDPVpnHandshake{ public_key: client_config.client.public_key.into_bytes() };
|
||||
sock_snd.send(&handshake.serialize()).await.unwrap();
|
||||
|
||||
loop {
|
||||
|
193
src/main.rs
193
src/main.rs
@ -1,13 +1,16 @@
|
||||
use tokio::{net::UdpSocket, sync::mpsc};
|
||||
use std::{fs, io::{self, Error, Read}, net::{IpAddr, SocketAddr}, sync::Arc, thread, time, str};
|
||||
use std::{fs, io::{self, Error, Read}, net::{IpAddr, Ipv4Addr, SocketAddr}, str, sync::Arc, thread, time};
|
||||
use std::process::Command;
|
||||
use clap::{App, Arg};
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use env_logger::Builder;
|
||||
use log::{error, info, warn, LevelFilter};
|
||||
use tun::platform::Device;
|
||||
use serde_derive::Serialize;
|
||||
use serde_derive::Deserialize;
|
||||
use std::str::FromStr;
|
||||
use x25519_dalek::{StaticSecret, PublicKey};
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
use base64;
|
||||
|
||||
//mod tcp_client;
|
||||
//mod tcp_server;
|
||||
@ -37,25 +40,26 @@ struct UDPVpnPacket {
|
||||
data: Vec<u8>
|
||||
}
|
||||
|
||||
impl UDPVpnPacket {
|
||||
impl UDPSerializable for UDPVpnPacket {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
let h: &[u8] = &[1];
|
||||
[h, &self.data[..]].concat()
|
||||
}
|
||||
}
|
||||
|
||||
struct UDPVpnHandshake {}
|
||||
struct UDPVpnHandshake {
|
||||
public_key: Vec<u8>
|
||||
}
|
||||
|
||||
impl UDPVpnHandshake {
|
||||
impl UDPSerializable for UDPVpnHandshake {
|
||||
fn serialize(&self) -> Vec<u8> {
|
||||
[0, 9, 9, 9, 9, 9, 9].to_vec()
|
||||
let h: &[u8] = &[0];
|
||||
[h, &self.public_key[..]].concat()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
enum ServerMode {
|
||||
VPN,
|
||||
Hotspot
|
||||
trait UDPSerializable {
|
||||
fn serialize(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
@ -63,7 +67,7 @@ struct ServerInterface {
|
||||
bind_address: String,
|
||||
internal_address: String,
|
||||
private_key: String,
|
||||
mode: ServerMode,
|
||||
public_key: String,
|
||||
broadcast_mode: bool,
|
||||
keepalive: u8
|
||||
}
|
||||
@ -71,7 +75,7 @@ struct ServerInterface {
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct ServerPeer {
|
||||
public_key: String,
|
||||
ip: IpAddr
|
||||
ip: Ipv4Addr
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
@ -95,17 +99,19 @@ pub struct ServerConfiguration {
|
||||
}
|
||||
|
||||
impl ServerConfiguration {
|
||||
fn default() -> Self {
|
||||
fn default(bind_address: &str, internal_address: &str, broadcast_mode: bool, keepalive: u8, obfs_type: ObfsProtocol) -> Self {
|
||||
let mut csprng = StdRng::from_entropy();
|
||||
let secret = StaticSecret::new(&mut csprng);
|
||||
ServerConfiguration { interface: ServerInterface {
|
||||
bind_address: String::from_str("0.0.0.0:8879").unwrap(),
|
||||
internal_address: String::from_str("10.8.0.1").unwrap(),
|
||||
private_key: String::new(),
|
||||
mode: ServerMode::VPN,
|
||||
broadcast_mode: true,
|
||||
keepalive: 10
|
||||
bind_address: String::from_str(bind_address).unwrap(),
|
||||
internal_address: String::from_str(internal_address).unwrap(),
|
||||
private_key: base64::encode(secret.as_bytes()),
|
||||
public_key: base64::encode(PublicKey::from(&secret).as_bytes()),
|
||||
broadcast_mode,
|
||||
keepalive
|
||||
},
|
||||
peers: Vec::new(),
|
||||
obfs: ObfsConfig { protocol: ObfsProtocol::DNSMask },
|
||||
obfs: ObfsConfig { protocol: obfs_type },
|
||||
dns: DNSConfig { enabled: false, net_name: String::from_str("fridah.vpn").unwrap(), entries: Vec::new() }
|
||||
}
|
||||
}
|
||||
@ -120,20 +126,22 @@ struct DNSConfig {
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct DNSEntry {
|
||||
ip: IpAddr,
|
||||
ip: Ipv4Addr,
|
||||
subdomain: String
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct ClientInterface {
|
||||
private_key: String,
|
||||
public_key: String,
|
||||
address: String
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct EndpointInterface {
|
||||
public_key: String,
|
||||
endpoint: String
|
||||
endpoint: String,
|
||||
keepalive: u8
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
@ -143,12 +151,81 @@ pub struct ClientConfiguration {
|
||||
}
|
||||
|
||||
impl ClientConfiguration {
|
||||
fn default() -> Self {
|
||||
ClientConfiguration { client: ClientInterface { private_key: String::new(), address: String::from_str("10.8.0.2").unwrap() },
|
||||
server: EndpointInterface { public_key: String::new(), endpoint: String::from_str("192.168.0.2:8879").unwrap() } }
|
||||
fn default(endpoint: &str, keepalive: u8, public_key: &str, internal_address: &str) -> Self {
|
||||
let mut csprng = StdRng::from_entropy();
|
||||
let secret = StaticSecret::new(&mut csprng);
|
||||
ClientConfiguration {
|
||||
client: ClientInterface {
|
||||
private_key: base64::encode(secret.as_bytes()),
|
||||
public_key: base64::encode(PublicKey::from(&secret).as_bytes()),
|
||||
address: String::from_str(internal_address).unwrap()
|
||||
},
|
||||
server: EndpointInterface {
|
||||
public_key: String::from_str(public_key).unwrap(),
|
||||
endpoint: String::from_str(endpoint).unwrap(),
|
||||
keepalive
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_server_config(matches: &ArgMatches, config_path: &str) {
|
||||
let bind_address = matches.value_of("bind-address").expect("No bind address specified");
|
||||
let internal_address = matches.value_of("internal-address").expect("No internal address specified");
|
||||
let broadcast_mode = matches.value_of("broadcast-mode").is_some();
|
||||
let keepalive: u8 = matches.value_of("keepalive").unwrap().parse().expect("Keepalive argument should be a number");
|
||||
let obfs_type = match matches.value_of("obfs-type").expect("Obfs type should be specified") {
|
||||
"dns" => ObfsProtocol::DNSMask,
|
||||
"icmp" => ObfsProtocol::ICMPMask,
|
||||
_ => ObfsProtocol::VEIL
|
||||
};
|
||||
|
||||
fs::write(config_path, serde_yaml::to_string(&ServerConfiguration::default(bind_address, internal_address, broadcast_mode, keepalive, obfs_type)).unwrap());
|
||||
}
|
||||
|
||||
fn generate_peer_config(matches: &ArgMatches, config_path: &str, cfg_raw: &String) {
|
||||
let keepalive: u8 = matches.value_of("keepalive").unwrap().parse().expect("Keepalive argument should be a number");
|
||||
let grab_endpoint = matches.value_of("grab-endpoint").is_some();
|
||||
let endpoint = matches.value_of("endpoint").or(Some("0.0.0.0:0")).unwrap();
|
||||
let peer_cfg = matches.value_of("peer-cfg").expect("No peer cfg path specified");
|
||||
|
||||
let mut config: ServerConfiguration = serde_yaml::from_str(cfg_raw).expect("Bad server config file structure");
|
||||
|
||||
let mut prs = &mut config.peers[..];
|
||||
prs.sort_by(|a, b| a.ip.octets()[3].cmp(&b.ip.octets()[3]));
|
||||
|
||||
let mut internal_address = prs.iter()
|
||||
.map(|p| p.ip)
|
||||
.collect::<Vec<Ipv4Addr>>()
|
||||
.first()
|
||||
.or(Some(&config.interface.internal_address.parse::<Ipv4Addr>().unwrap()))
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
internal_address = Ipv4Addr::new(internal_address.octets()[0], internal_address.octets()[1], internal_address.octets()[2], internal_address.octets()[3]+1);
|
||||
|
||||
let cl_cfg = &ClientConfiguration::default(if grab_endpoint { &config.interface.bind_address } else { endpoint },
|
||||
keepalive,
|
||||
&config.interface.public_key,
|
||||
&internal_address.to_string());
|
||||
|
||||
config.peers.push(ServerPeer { public_key: cl_cfg.client.public_key.clone(), ip: internal_address.clone() });
|
||||
|
||||
fs::write(peer_cfg, serde_yaml::to_string(cl_cfg).unwrap());
|
||||
|
||||
fs::write(config_path, serde_yaml::to_string(&config).unwrap());
|
||||
}
|
||||
|
||||
async fn init_server(cfg_raw: &str ) {
|
||||
let config: ServerConfiguration = serde_yaml::from_str(cfg_raw).expect("Bad server config file structure");
|
||||
server::server_mode(config).await;
|
||||
}
|
||||
|
||||
async fn init_client(cfg_raw: &str) {
|
||||
let config: ClientConfiguration = serde_yaml::from_str(cfg_raw).expect("Bad client config file structure");
|
||||
client::client_mode(config).await;
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
|
||||
@ -164,7 +241,7 @@ async fn main() {
|
||||
.arg(Arg::with_name("mode")
|
||||
.required(true)
|
||||
.index(1)
|
||||
.possible_values(&["server", "client"])
|
||||
.possible_values(&["server", "client", "gen_cfg", "new_peer"])
|
||||
.help("Runs the program in either server or client mode"))
|
||||
.arg(Arg::with_name("config")
|
||||
.long("config")
|
||||
@ -172,30 +249,70 @@ async fn main() {
|
||||
.value_name("FILE")
|
||||
.help("The path to VPN configuration file")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("peer-cfg")
|
||||
.long("peer-cfg")
|
||||
.value_name("FILE")
|
||||
.help("The path to VPN peer configuration file")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("bind-address")
|
||||
.long("bind-address")
|
||||
.value_name("IP:PORT")
|
||||
.help("The ip:port that would be used to bind server (config)")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("endpoint")
|
||||
.long("endpoint")
|
||||
.value_name("IP:PORT")
|
||||
.help("The ip:port that would be used by client to connect (config)")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("internal-address")
|
||||
.long("internal-address")
|
||||
.value_name("IP")
|
||||
.help("The address of VPN server in it's subnet (config)")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("broadcast-mode")
|
||||
.long("broadcast-mode")
|
||||
.help("If set to true, then all incoming traffic with an unknown destination address will be forwarded to all peers (config)")
|
||||
.takes_value(false))
|
||||
.arg(Arg::with_name("grab-endpoint")
|
||||
.long("grab-endpoint")
|
||||
.help("If set to true, the endpoint address for peers will be grabbed from server config (config)")
|
||||
.takes_value(false))
|
||||
.arg(Arg::with_name("keepalive")
|
||||
.long("keepalive")
|
||||
.required(false)
|
||||
.value_name("SECONDS")
|
||||
.default_value("0")
|
||||
.help("Keepalive packets interval (config)")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("obfs-type")
|
||||
.long("obfs-type")
|
||||
.possible_values(&["dns", "icmp", "veil"])
|
||||
.takes_value(true)
|
||||
.value_name("OBFS")
|
||||
.help("Obfuscation protocol (config)"))
|
||||
.get_matches();
|
||||
|
||||
let is_server_mode = matches.value_of("mode").unwrap() == "server";
|
||||
let mode = matches.value_of("mode").unwrap();
|
||||
|
||||
if let Some(config_path) = matches.value_of("config") {
|
||||
|
||||
let data = fs::read(config_path);
|
||||
|
||||
if data.is_err() {
|
||||
warn!("There is no config file. Generating it.");
|
||||
if is_server_mode {
|
||||
fs::write(config_path, serde_yaml::to_string(&ServerConfiguration::default()).unwrap());
|
||||
return;
|
||||
match mode {
|
||||
"gen_cfg" => generate_server_config(&matches, config_path),
|
||||
_ => error!("There is no config file.")
|
||||
}
|
||||
fs::write(config_path, serde_yaml::to_string(&ClientConfiguration::default()).unwrap());
|
||||
return;
|
||||
}
|
||||
|
||||
if is_server_mode {
|
||||
let config: ServerConfiguration = serde_yaml::from_str(&String::from_utf8(data.unwrap()).unwrap()).unwrap();
|
||||
server::server_mode(config).await;
|
||||
return;
|
||||
let cfg_raw = &String::from_utf8(data.unwrap()).unwrap();
|
||||
|
||||
match mode {
|
||||
"server" => init_server(cfg_raw).await,
|
||||
"client" => init_client(cfg_raw).await,
|
||||
"new_peer" => generate_peer_config(&matches, config_path, cfg_raw),
|
||||
_ => error!("There is config file already")
|
||||
}
|
||||
let config: ClientConfiguration = serde_yaml::from_str(&String::from_utf8(data.unwrap()).unwrap()).unwrap();
|
||||
client::client_mode(config).await;
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ use std::collections::HashMap;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use std::process::Command;
|
||||
|
||||
use crate::{ VpnPacket, ServerConfiguration };
|
||||
use crate::{ VpnPacket, ServerConfiguration, UDPSerializable };
|
||||
|
||||
pub async fn server_mode(server_config: ServerConfiguration) {
|
||||
info!("Starting server...");
|
||||
|
Loading…
x
Reference in New Issue
Block a user