From 8b79f11b397b8aeb8fa18f0cdf804164bbc1feda Mon Sep 17 00:00:00 2001 From: Andrey Golovizin Date: Sun, 9 Apr 2023 14:15:48 +0200 Subject: [PATCH] Migrate to API v2 --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 122 ++++++++++++++++++++-------------------------------- 3 files changed, 48 insertions(+), 78 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 75d572f..265dccf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,7 +25,7 @@ checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "azirevpn" -version = "0.1.0" +version = "0.2.0" dependencies = [ "anyhow", "clap", diff --git a/Cargo.toml b/Cargo.toml index 7b1e075..edf2c31 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "azirevpn" -version = "0.1.0" +version = "0.2.0" authors = ["Andrey Golovizin "] edition = "2021" license = "MIT" diff --git a/src/main.rs b/src/main.rs index 1224d3b..5fe4d8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use std::io::Write; -use std::net::{AddrParseError, IpAddr, ToSocketAddrs}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::process; use log::debug; @@ -7,7 +7,7 @@ use log::debug; use clap::{Parser, Subcommand}; use serde::{Deserialize, Serialize}; -const BASE_URL: &str = "https://api.azirevpn.com/v1"; +const BASE_URL: &str = "https://api.azirevpn.com/v2"; /// AzireVPN client #[derive(Parser, Debug)] @@ -37,18 +37,16 @@ struct ConfigOpts { #[derive(Subcommand, Debug)] enum Command { /// Prints the list of VPN endpoints - Endpoints, + Locations, /// Prints WireGuard config Config(ConfigOpts), - - /// Checks connection status - Check, } #[derive(Serialize, Deserialize, Debug)] -struct Endpoints { - wireguard: String, +struct Locations { + status: String, + locations: Vec, } #[derive(Serialize, Deserialize, Debug)] @@ -57,36 +55,27 @@ struct Location { city: String, country: String, iso: String, - endpoints: Endpoints, -} - -#[derive(Serialize, Deserialize, Debug)] -struct Locations { - locations: Vec, -} - -#[derive(Serialize, Deserialize, Debug)] -struct CheckResult { - connected: bool, - ip: String, // XXX - location: Option, + pool: String, + pubkey: String, } #[derive(Serialize, Deserialize, Debug)] struct WireguardConfig { status: String, - data: WireguardConfigData, + ipv4: WireguardConfigIpv4, + ipv6: WireguardConfigIpv6, + dns: Vec, } #[derive(Serialize, Deserialize, Debug)] -#[serde(rename_all = "PascalCase")] -struct WireguardConfigData { - public_key: String, - address: String, - endpoint: String, - - #[serde(rename = "DNS")] - dns: String, +struct WireguardConfigIpv4 { + address: Ipv4Addr, + netmask: u8, +} +#[derive(Serialize, Deserialize, Debug)] +struct WireguardConfigIpv6 { + address: Ipv6Addr, + netmask: u8, } #[derive(Debug)] @@ -95,34 +84,17 @@ struct WireguardKeyPair { private_key: String, } -impl WireguardConfigData { - fn addresses(&self) -> Result, ipnet::AddrParseError> { - self.address - .split(',') - .map(|s: &str| -> Result { s.trim().parse() }) - .collect() - } - - fn dns(&self) -> Result, AddrParseError> { - self.dns - .split(',') - .map(|s: &str| -> Result { s.trim().parse() }) - .collect() - } -} - fn main() -> Result<(), anyhow::Error> { env_logger::init(); let opts = Opts::parse(); 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::Check => check(&opts)?, } Ok(()) } -fn list_endpoints(opts: &Opts) -> Result<(), anyhow::Error> { +fn list_locations(opts: &Opts) -> Result<(), anyhow::Error> { let locations: Locations = get_locations()?; if opts.json { println!("{}", serde_json::to_string(&locations)?); @@ -135,7 +107,8 @@ fn list_endpoints(opts: &Opts) -> Result<(), anyhow::Error> { println!("City: {}", location.city); println!("Country: {}", location.country); println!("Country code: {}", location.iso); - println!("WireGuard endpoint: {}", location.endpoints.wireguard); + println!("Pool: {}", location.pool); + println!("Pubkey: {}", location.pubkey); } } Ok(()) @@ -151,33 +124,46 @@ fn get_config(_opts: &Opts, config_opts: &ConfigOpts) -> Result<(), anyhow::Erro debug!("location = {:?}", &location); let keys = generage_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(&[ ("username", &config_opts.username), ("token", &config_opts.token), - ("pubkey", &keys.public_key), + ("key", &keys.public_key), ])? .into_json()?; + debug!("response = {:?}", &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( output: &mut dyn Write, config_opts: &ConfigOpts, + location: &Location, config: &WireguardConfig, keys: &WireguardKeyPair, ) -> Result<(), anyhow::Error> { writeln!(output, "[Interface]")?; writeln!(output, "PrivateKey = {}", &keys.private_key)?; - let addresses = config.data.addresses()?; - let allowed_addresses = addresses - .iter() - .filter(|addr| addr.addr().is_ipv4() || !config_opts.no_ipv6); + let allowed_addresses = if config_opts.no_ipv6 { + vec![IpAddr::V4(config.ipv4.address)] + } else { + vec![ + IpAddr::V4(config.ipv4.address), + IpAddr::V6(config.ipv6.address), + ] + }; write_list(output, "Address = ", allowed_addresses)?; if !config_opts.no_dns { - let dns_addrs = config.data.dns()?; + let dns_addrs = &config.dns; let allowed_dns_addrs = dns_addrs .iter() .filter(|addr| addr.is_ipv4() || !config_opts.no_ipv6); @@ -186,10 +172,9 @@ fn write_config( writeln!(output)?; writeln!(output, "[Peer]")?; - writeln!(output, "PublicKey = {}", &config.data.public_key)?; + writeln!(output, "PublicKey = {}", &location.pubkey)?; - let endpoint_addrs = config.data.endpoint.to_socket_addrs()?; - write_list(output, "Endpoint = ", endpoint_addrs)?; + writeln!(output, "Endpoint = {}:51820", &location.pool)?; let allowed_ips: &[&str] = if config_opts.no_ipv6 { &["0.0.0.0/0"] @@ -217,21 +202,6 @@ where 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 { let url = format!("{}/locations", BASE_URL); let locations: Locations = ureq::get(&url).call()?.into_json()?;