Migrate to API v2

This commit is contained in:
Andrey Golovizin 2023-04-09 14:15:48 +02:00
parent 61bb0dd397
commit 8b79f11b39
3 changed files with 48 additions and 78 deletions

View file

@ -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<Location>,
}
#[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<Location>,
}
#[derive(Serialize, Deserialize, Debug)]
struct CheckResult {
connected: bool,
ip: String, // XXX
location: Option<String>,
pool: String,
pubkey: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct WireguardConfig {
status: String,
data: WireguardConfigData,
ipv4: WireguardConfigIpv4,
ipv6: WireguardConfigIpv6,
dns: Vec<IpAddr>,
}
#[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<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> {
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<Locations, anyhow::Error> {
let url = format!("{}/locations", BASE_URL);
let locations: Locations = ureq::get(&url).call()?.into_json()?;