Changes to be committed:

modified:   Cargo.lock
	modified:   Cargo.toml
	modified:   README.md
	modified:   src/client.rs
	modified:   src/main.rs
	modified:   src/server.rs
This commit is contained in:
Michael Wain 2024-08-17 20:33:20 +03:00
parent f24dab6188
commit 8bf6d57ba8
6 changed files with 243 additions and 67 deletions

56
Cargo.lock generated
View File

@ -414,6 +414,12 @@ dependencies = [
"termcolor",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.9"
@ -556,6 +562,12 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -601,6 +613,16 @@ dependencies = [
"phf",
]
[[package]]
name = "indexmap"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "inout"
version = "0.1.3"
@ -640,6 +662,12 @@ dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "lazy_static"
version = "1.5.0"
@ -1201,7 +1229,7 @@ dependencies = [
[[package]]
name = "rustvpn"
version = "0.1.0"
version = "0.1.2"
dependencies = [
"aes 0.7.5",
"aes-gcm",
@ -1225,12 +1253,19 @@ dependencies = [
"rand",
"serde",
"serde_derive",
"serde_yaml",
"socket2 0.4.10",
"tokio",
"tun",
"tun2",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "scopeguard"
version = "1.2.0"
@ -1257,6 +1292,19 @@ dependencies = [
"syn 2.0.72",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "shlex"
version = "1.3.0"
@ -1473,6 +1521,12 @@ dependencies = [
"subtle",
]
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "vec_map"
version = "0.8.2"

View File

@ -1,6 +1,6 @@
[package]
name = "rustvpn"
version = "0.1.0"
version = "0.1.2"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -31,4 +31,5 @@ ctrlc2 = "3.5"
crossbeam-channel = "0.5.13"
pnet = "0.35.0"
net-route = "0.4.4"
hex = "0.4"
hex = "0.4"
serde_yaml = "0.9.34"

View File

@ -1,4 +1,34 @@
# Frida
A basic VPN tunnel with data obfuscation.
![GitHub last commit](https://img.shields.io/github/last-commit/alterdekim/Frida)
![GitHub Release Date](https://img.shields.io/github/release-date/alterdekim/Frida)
![Jenkins Build](https://img.shields.io/jenkins/build)
![docs.rs](https://img.shields.io/docsrs/:crate)
A lightweight VPN software, focused on scalability, traffic obfuscation and simplicity.
## Documentation
Check the [repository wiki]() to access the documentation, tutorials.
## Installation
On Linux, you can run this in a terminal (sudo required):
```
curl --proto '=https' --tlsv1.2 -sSf https://get-frida.awain.net | sh
```
Also you can download latest version from the releases page.
## Android / IOS
There is an app for both Android and IOS devices.
### Android links
- Google play: ...
- Github: ...
### IOS links
- Github: ...

View File

@ -11,7 +11,7 @@ use std::collections::HashMap;
use std::process::Command;
use tokio::io::AsyncReadExt;
use crate::{UDPVpnHandshake, UDPVpnPacket, VpnPacket};
use crate::{UDPVpnHandshake, UDPVpnPacket, VpnPacket, ClientConfiguration};
fn configure_routes() {
let ip_output = Command::new("ip")
@ -58,15 +58,15 @@ fn configure_routes() {
}
}
pub async fn client_mode(remote_addr: String) {
pub async fn client_mode(client_config: ClientConfiguration) {
info!("Starting client...");
let mut config = tun2::Configuration::default();
config.address("10.8.0.2");
config.netmask("128.0.0.0");
config.destination("0.0.0.0");
config.name("tun0");
config.up();
config.address(&client_config.client.address)
.netmask("128.0.0.0")
.destination("0.0.0.0")
.name("tun0")
.up();
#[cfg(target_os = "linux")]
config.platform_config(|config| {
@ -80,7 +80,7 @@ pub async fn client_mode(remote_addr: String) {
configure_routes();
let sock = UdpSocket::bind("0.0.0.0:59611").await.unwrap();
sock.connect(&remote_addr).await.unwrap();
sock.connect(&client_config.server.endpoint).await.unwrap();
let sock_rec = Arc::new(sock);
let sock_snd = sock_rec.clone();

View File

@ -1,22 +1,21 @@
use tokio::{net::UdpSocket, sync::mpsc};
use std::{io::{self, Error, Read}, net::SocketAddr, sync::Arc, thread, time};
use std::{fs, io::{self, Error, Read}, net::{IpAddr, SocketAddr}, sync::Arc, thread, time, str};
use std::process::Command;
use clap::{App, Arg};
use env_logger::Builder;
use log::{error, info, LevelFilter};
use log::{error, info, warn, LevelFilter};
use tun::platform::Device;
use serde_derive::Serialize;
use serde_derive::Deserialize;
use std::str::FromStr;
mod tcp_client;
mod tcp_server;
//mod tcp_client;
//mod tcp_server;
mod server;
mod client;
struct VpnPacket {
//start: Vec<u8>
data: Vec<u8>
//end: Vec<u8>
}
impl VpnPacket {
@ -34,11 +33,8 @@ impl VpnPacket {
}
}
struct UDPVpnPacket {
//start: Vec<u8>
data: Vec<u8>
//end: Vec<u8>
}
impl UDPVpnPacket {
@ -56,6 +52,103 @@ impl UDPVpnHandshake {
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum ServerMode {
VPN,
Hotspot
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct ServerInterface {
bind_address: String,
internal_address: String,
private_key: String,
mode: ServerMode,
broadcast_mode: bool,
keepalive: u8
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct ServerPeer {
public_key: String,
ip: IpAddr
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum ObfsProtocol {
DNSMask,
ICMPMask,
VEIL
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct ObfsConfig {
protocol: ObfsProtocol
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct ServerConfiguration {
interface: ServerInterface,
peers: Vec<ServerPeer>,
obfs: ObfsConfig,
dns: DNSConfig
}
impl ServerConfiguration {
fn default() -> Self {
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
},
peers: Vec::new(),
obfs: ObfsConfig { protocol: ObfsProtocol::DNSMask },
dns: DNSConfig { enabled: false, net_name: String::from_str("fridah.vpn").unwrap(), entries: Vec::new() }
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct DNSConfig {
enabled: bool,
net_name: String,
entries: Vec<DNSEntry>
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct DNSEntry {
ip: IpAddr,
subdomain: String
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct ClientInterface {
private_key: String,
address: String
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct EndpointInterface {
public_key: String,
endpoint: String
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct ClientConfiguration {
client: ClientInterface,
server: EndpointInterface
}
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() } }
}
}
#[tokio::main]
async fn main() {
@ -65,7 +158,7 @@ async fn main() {
.init();
let matches = App::new("Frida VPN")
.version("1.0")
.version("0.1.2")
.author("alterwain")
.about("VPN software")
.arg(Arg::with_name("mode")
@ -73,34 +166,36 @@ async fn main() {
.index(1)
.possible_values(&["server", "client"])
.help("Runs the program in either server or client mode"))
.arg(Arg::with_name("vpn-server")
.long("vpn-server")
.value_name("IP")
.help("The IP address of the VPN server to connect to (client mode only)")
.takes_value(true))
.arg(Arg::with_name("bind-to")
.long("bind-to")
.value_name("IP")
.help("The IP address of the VPN server to bind to (server mode only)")
.arg(Arg::with_name("config")
.long("config")
.required(true)
.value_name("FILE")
.help("The path to VPN configuration file")
.takes_value(true))
.get_matches();
let is_server_mode = matches.value_of("mode").unwrap() == "server";
// "192.168.0.4:8879"
if is_server_mode {
if let Some(vpn_server_ip) = matches.value_of("bind-to") {
let server_address = format!("{}:8879", vpn_server_ip);
server::server_mode(server_address).await;
} else {
eprintln!("Error: For server mode, you shall provide the '--bind-to' argument.");
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;
}
fs::write(config_path, serde_yaml::to_string(&ClientConfiguration::default()).unwrap());
return;
}
} else {
if let Some(vpn_server_ip) = matches.value_of("vpn-server") {
let server_address = format!("{}:8879", vpn_server_ip);
client::client_mode(server_address).await;
} else {
eprintln!("Error: For client mode, you shall provide the '--vpn-server' argument.");
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 config: ClientConfiguration = serde_yaml::from_str(&String::from_utf8(data.unwrap()).unwrap()).unwrap();
client::client_mode(config).await;
}
}

View File

@ -11,16 +11,16 @@ use std::collections::HashMap;
use tokio::io::AsyncReadExt;
use std::process::Command;
use crate::VpnPacket;
use crate::{ VpnPacket, ServerConfiguration };
pub async fn server_mode(bind_addr: String) {
pub async fn server_mode(server_config: ServerConfiguration) {
info!("Starting server...");
let mut config = tun2::Configuration::default();
config.address("10.8.0.1");
config.netmask("255.255.255.0");
config.tun_name("tun0");
config.up();
config.address(&server_config.interface.internal_address)
.netmask("255.255.255.0")
.tun_name("tun0")
.up();
#[cfg(target_os = "linux")]
config.platform_config(|config| {
@ -30,7 +30,7 @@ pub async fn server_mode(bind_addr: String) {
let dev = tun2::create(&config).unwrap();
let (mut dev_reader, mut dev_writer) = dev.split();
let sock = UdpSocket::bind(bind_addr).await.unwrap();
let sock = UdpSocket::bind(&server_config.interface.bind_address).await.unwrap();
let sock_rec = Arc::new(sock);
let sock_snd = sock_rec.clone();
let addresses = Arc::new(Mutex::new(HashMap::<IpAddr, UDPeer>::new()));
@ -49,19 +49,17 @@ pub async fn server_mode(bind_addr: String) {
tokio::spawn(async move {
let mut buf = vec![0; 4096];
while let Ok(n) = dev_reader.read(&mut buf) {
// 16..=19
if n > 19 {
let ip = IpAddr::V4(Ipv4Addr::new(buf[16], buf[17], buf[18], buf[19]));
let mp = addrs_cl.lock().await;
if let Some(peer) = mp.get(&ip) {
//info!("Sent to client");
sock_snd.send_to(&buf[..n], peer.addr).await;
} else {
mp.values().for_each(| peer | { sock_snd.send_to(&buf[..n], peer.addr); });
error!("UDPeer not found {:?}; what we have {:?}", ip, mp.keys().collect::<Vec<&IpAddr>>());
}
drop(mp);
if n <= 19 { continue; }
let ip = IpAddr::V4(Ipv4Addr::new(buf[16], buf[17], buf[18], buf[19]));
let mp = addrs_cl.lock().await;
if let Some(peer) = mp.get(&ip) {
sock_snd.send_to(&buf[..n], peer.addr).await;
} else {
// TODO: check in config is broadcast mode enabled (if not, do not send this to everyone)
mp.values().for_each(| peer | { sock_snd.send_to(&buf[..n], peer.addr); });
}
drop(mp);
}
});
@ -85,9 +83,7 @@ pub async fn server_mode(bind_addr: String) {
send2tun.send((&buf[1..len]).to_vec());
}
}, // payload
_ => {
error!("Unexpected header value.");
}
_ => error!("Unexpected header value.")
}
},
None => error!("There is no header")