Migrate to API v2
This commit is contained in:
parent
61bb0dd397
commit
8b79f11b39
3 changed files with 48 additions and 78 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -25,7 +25,7 @@ checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "azirevpn"
|
name = "azirevpn"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "azirevpn"
|
name = "azirevpn"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
authors = ["Andrey Golovizin <ag@sologoc.com>"]
|
authors = ["Andrey Golovizin <ag@sologoc.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|
|
||||||
122
src/main.rs
122
src/main.rs
|
|
@ -1,5 +1,5 @@
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::net::{AddrParseError, IpAddr, ToSocketAddrs};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||||
use std::process;
|
use std::process;
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
@ -7,7 +7,7 @@ use log::debug;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
const BASE_URL: &str = "https://api.azirevpn.com/v1";
|
const BASE_URL: &str = "https://api.azirevpn.com/v2";
|
||||||
|
|
||||||
/// AzireVPN client
|
/// AzireVPN client
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
|
@ -37,18 +37,16 @@ struct ConfigOpts {
|
||||||
#[derive(Subcommand, Debug)]
|
#[derive(Subcommand, Debug)]
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Prints the list of VPN endpoints
|
/// Prints the list of VPN endpoints
|
||||||
Endpoints,
|
Locations,
|
||||||
|
|
||||||
/// Prints WireGuard config
|
/// Prints WireGuard config
|
||||||
Config(ConfigOpts),
|
Config(ConfigOpts),
|
||||||
|
|
||||||
/// Checks connection status
|
|
||||||
Check,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
struct Endpoints {
|
struct Locations {
|
||||||
wireguard: String,
|
status: String,
|
||||||
|
locations: Vec<Location>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
@ -57,36 +55,27 @@ struct Location {
|
||||||
city: String,
|
city: String,
|
||||||
country: String,
|
country: String,
|
||||||
iso: String,
|
iso: String,
|
||||||
endpoints: Endpoints,
|
pool: String,
|
||||||
}
|
pubkey: String,
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct Locations {
|
|
||||||
locations: Vec<Location>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
struct CheckResult {
|
|
||||||
connected: bool,
|
|
||||||
ip: String, // XXX
|
|
||||||
location: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
struct WireguardConfig {
|
struct WireguardConfig {
|
||||||
status: String,
|
status: String,
|
||||||
data: WireguardConfigData,
|
ipv4: WireguardConfigIpv4,
|
||||||
|
ipv6: WireguardConfigIpv6,
|
||||||
|
dns: Vec<IpAddr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
struct WireguardConfigIpv4 {
|
||||||
struct WireguardConfigData {
|
address: Ipv4Addr,
|
||||||
public_key: String,
|
netmask: u8,
|
||||||
address: String,
|
}
|
||||||
endpoint: String,
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct WireguardConfigIpv6 {
|
||||||
#[serde(rename = "DNS")]
|
address: Ipv6Addr,
|
||||||
dns: String,
|
netmask: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -95,34 +84,17 @@ struct WireguardKeyPair {
|
||||||
private_key: String,
|
private_key: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WireguardConfigData {
|
|
||||||
fn addresses(&self) -> Result<Vec<ipnet::IpNet>, ipnet::AddrParseError> {
|
|
||||||
self.address
|
|
||||||
.split(',')
|
|
||||||
.map(|s: &str| -> Result<ipnet::IpNet, ipnet::AddrParseError> { s.trim().parse() })
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dns(&self) -> Result<Vec<IpAddr>, AddrParseError> {
|
|
||||||
self.dns
|
|
||||||
.split(',')
|
|
||||||
.map(|s: &str| -> Result<IpAddr, AddrParseError> { s.trim().parse() })
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), anyhow::Error> {
|
fn main() -> Result<(), anyhow::Error> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
let opts = Opts::parse();
|
let opts = Opts::parse();
|
||||||
match &opts.command {
|
match &opts.command {
|
||||||
Command::Endpoints => list_endpoints(&opts)?,
|
Command::Locations => list_locations(&opts)?,
|
||||||
Command::Config(get_config_opts) => get_config(&opts, get_config_opts)?,
|
Command::Config(get_config_opts) => get_config(&opts, get_config_opts)?,
|
||||||
Command::Check => check(&opts)?,
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn list_endpoints(opts: &Opts) -> Result<(), anyhow::Error> {
|
fn list_locations(opts: &Opts) -> Result<(), anyhow::Error> {
|
||||||
let locations: Locations = get_locations()?;
|
let locations: Locations = get_locations()?;
|
||||||
if opts.json {
|
if opts.json {
|
||||||
println!("{}", serde_json::to_string(&locations)?);
|
println!("{}", serde_json::to_string(&locations)?);
|
||||||
|
|
@ -135,7 +107,8 @@ fn list_endpoints(opts: &Opts) -> Result<(), anyhow::Error> {
|
||||||
println!("City: {}", location.city);
|
println!("City: {}", location.city);
|
||||||
println!("Country: {}", location.country);
|
println!("Country: {}", location.country);
|
||||||
println!("Country code: {}", location.iso);
|
println!("Country code: {}", location.iso);
|
||||||
println!("WireGuard endpoint: {}", location.endpoints.wireguard);
|
println!("Pool: {}", location.pool);
|
||||||
|
println!("Pubkey: {}", location.pubkey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -151,33 +124,46 @@ fn get_config(_opts: &Opts, config_opts: &ConfigOpts) -> Result<(), anyhow::Erro
|
||||||
debug!("location = {:?}", &location);
|
debug!("location = {:?}", &location);
|
||||||
let keys = generage_keys()?;
|
let keys = generage_keys()?;
|
||||||
debug!("keys = {:?}", &keys);
|
debug!("keys = {:?}", &keys);
|
||||||
let config: WireguardConfig = ureq::post(&location.endpoints.wireguard)
|
let url = format!("{}/ip/add", BASE_URL);
|
||||||
|
let config: WireguardConfig = ureq::post(&url)
|
||||||
.send_form(&[
|
.send_form(&[
|
||||||
("username", &config_opts.username),
|
("username", &config_opts.username),
|
||||||
("token", &config_opts.token),
|
("token", &config_opts.token),
|
||||||
("pubkey", &keys.public_key),
|
("key", &keys.public_key),
|
||||||
])?
|
])?
|
||||||
.into_json()?;
|
.into_json()?;
|
||||||
|
debug!("response = {:?}", &config);
|
||||||
debug!("config = {:?}", &config);
|
debug!("config = {:?}", &config);
|
||||||
|
|
||||||
write_config(&mut std::io::stdout().lock(), config_opts, &config, &keys)
|
write_config(
|
||||||
|
&mut std::io::stdout().lock(),
|
||||||
|
config_opts,
|
||||||
|
location,
|
||||||
|
&config,
|
||||||
|
&keys,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_config(
|
fn write_config(
|
||||||
output: &mut dyn Write,
|
output: &mut dyn Write,
|
||||||
config_opts: &ConfigOpts,
|
config_opts: &ConfigOpts,
|
||||||
|
location: &Location,
|
||||||
config: &WireguardConfig,
|
config: &WireguardConfig,
|
||||||
keys: &WireguardKeyPair,
|
keys: &WireguardKeyPair,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
writeln!(output, "[Interface]")?;
|
writeln!(output, "[Interface]")?;
|
||||||
writeln!(output, "PrivateKey = {}", &keys.private_key)?;
|
writeln!(output, "PrivateKey = {}", &keys.private_key)?;
|
||||||
let addresses = config.data.addresses()?;
|
let allowed_addresses = if config_opts.no_ipv6 {
|
||||||
let allowed_addresses = addresses
|
vec![IpAddr::V4(config.ipv4.address)]
|
||||||
.iter()
|
} else {
|
||||||
.filter(|addr| addr.addr().is_ipv4() || !config_opts.no_ipv6);
|
vec![
|
||||||
|
IpAddr::V4(config.ipv4.address),
|
||||||
|
IpAddr::V6(config.ipv6.address),
|
||||||
|
]
|
||||||
|
};
|
||||||
write_list(output, "Address = ", allowed_addresses)?;
|
write_list(output, "Address = ", allowed_addresses)?;
|
||||||
if !config_opts.no_dns {
|
if !config_opts.no_dns {
|
||||||
let dns_addrs = config.data.dns()?;
|
let dns_addrs = &config.dns;
|
||||||
let allowed_dns_addrs = dns_addrs
|
let allowed_dns_addrs = dns_addrs
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|addr| addr.is_ipv4() || !config_opts.no_ipv6);
|
.filter(|addr| addr.is_ipv4() || !config_opts.no_ipv6);
|
||||||
|
|
@ -186,10 +172,9 @@ fn write_config(
|
||||||
writeln!(output)?;
|
writeln!(output)?;
|
||||||
|
|
||||||
writeln!(output, "[Peer]")?;
|
writeln!(output, "[Peer]")?;
|
||||||
writeln!(output, "PublicKey = {}", &config.data.public_key)?;
|
writeln!(output, "PublicKey = {}", &location.pubkey)?;
|
||||||
|
|
||||||
let endpoint_addrs = config.data.endpoint.to_socket_addrs()?;
|
writeln!(output, "Endpoint = {}:51820", &location.pool)?;
|
||||||
write_list(output, "Endpoint = ", endpoint_addrs)?;
|
|
||||||
|
|
||||||
let allowed_ips: &[&str] = if config_opts.no_ipv6 {
|
let allowed_ips: &[&str] = if config_opts.no_ipv6 {
|
||||||
&["0.0.0.0/0"]
|
&["0.0.0.0/0"]
|
||||||
|
|
@ -217,21 +202,6 @@ where
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check(opts: &Opts) -> Result<(), anyhow::Error> {
|
|
||||||
let url = format!("{}/check", BASE_URL);
|
|
||||||
let result: CheckResult = ureq::get(&url).call()?.into_json()?;
|
|
||||||
if opts.json {
|
|
||||||
println!("{}", serde_json::to_string(&result)?);
|
|
||||||
} else {
|
|
||||||
println!("Connected: {:?}", result.connected);
|
|
||||||
println!("IP: {}", result.ip);
|
|
||||||
if let Some(location) = result.location {
|
|
||||||
println!("Location: {}", location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_locations() -> Result<Locations, anyhow::Error> {
|
fn get_locations() -> Result<Locations, anyhow::Error> {
|
||||||
let url = format!("{}/locations", BASE_URL);
|
let url = format!("{}/locations", BASE_URL);
|
||||||
let locations: Locations = ureq::get(&url).call()?.into_json()?;
|
let locations: Locations = ureq::get(&url).call()?.into_json()?;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue