|
1 |
| -use super::Mullvad; |
| 1 | +use super::AzireVPN; |
2 | 2 | use super::{ConfigurationChoice, OpenVpnProvider};
|
3 | 3 | use crate::util::delete_all_files_in_dir;
|
4 | 4 | use crate::vpn::OpenVpnProtocol;
|
5 |
| -use anyhow::Context; |
6 |
| -use log::warn; |
7 |
| -use rand::seq::SliceRandom; |
8 |
| -use reqwest::blocking::Client; |
9 |
| -use serde::Deserialize; |
10 |
| -use std::collections::HashMap; |
11 |
| -use std::fmt::Display; |
| 5 | +use log::{debug, info}; |
12 | 6 | use std::fs::create_dir_all;
|
13 | 7 | use std::fs::File;
|
14 | 8 | use std::io::Write;
|
15 | 9 | use std::net::{IpAddr, Ipv4Addr};
|
16 | 10 | use std::path::PathBuf;
|
17 |
| -use strum::IntoEnumIterator; |
18 |
| -use strum_macros::EnumIter; |
19 | 11 |
|
20 |
| -impl Mullvad { |
21 |
| - fn get_default_openvpn_settings(&self) -> Vec<&'static str> { |
22 |
| - vec![ |
23 |
| - "client", |
24 |
| - "dev tun", |
25 |
| - "resolv-retry infinite", |
26 |
| - "nobind", |
27 |
| - "persist-key", |
28 |
| - "persist-tun", |
29 |
| - "verb 3", |
30 |
| - "remote-cert-tls server", |
31 |
| - "ping 10", |
32 |
| - "ping-restart 60", |
33 |
| - "sndbuf 524288", |
34 |
| - "rcvbuf 524288", |
35 |
| - "cipher AES-256-CBC", |
36 |
| - "tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA", |
37 |
| - "auth-user-pass mullvad_userpass.txt", |
38 |
| - "ca mullvad_ca.crt", |
39 |
| - "tun-ipv6", |
40 |
| - "script-security 2", |
41 |
| - ] |
42 |
| - } |
43 |
| -} |
44 |
| - |
45 |
| -impl OpenVpnProvider for Mullvad { |
| 12 | +impl OpenVpnProvider for AzireVPN { |
| 13 | + // AzireVPN details: https://www.azirevpn.com/docs/servers |
| 14 | + // TODO: Add IPv6 DNS |
46 | 15 | fn provider_dns(&self) -> Option<Vec<IpAddr>> {
|
47 |
| - Some(vec![IpAddr::V4(Ipv4Addr::new(193, 138, 218, 74))]) |
| 16 | + Some(vec![ |
| 17 | + IpAddr::V4(Ipv4Addr::new(91, 231, 153, 2)), |
| 18 | + IpAddr::V4(Ipv4Addr::new(192, 211, 0, 2)), |
| 19 | + ]) |
48 | 20 | }
|
49 | 21 |
|
50 | 22 | fn prompt_for_auth(&self) -> anyhow::Result<(String, String)> {
|
51 |
| - let username = self.request_mullvad_username()?; |
52 |
| - Ok((username, "m".to_string())) |
| 23 | + self.request_userpass() |
53 | 24 | }
|
54 | 25 |
|
55 | 26 | fn auth_file_path(&self) -> anyhow::Result<PathBuf> {
|
56 |
| - Ok(self.openvpn_dir()?.join("mullvad_userpass.txt")) |
| 27 | + Ok(self.openvpn_dir()?.join("auth.txt")) |
57 | 28 | }
|
58 | 29 |
|
59 | 30 | fn create_openvpn_config(&self) -> anyhow::Result<()> {
|
| 31 | + let protocol = OpenVpnProtocol::choose_one()?; |
| 32 | + // TODO: Allow port selection, TLS version selection |
60 | 33 | let openvpn_dir = self.openvpn_dir()?;
|
| 34 | + let country_map = crate::util::country_map::code_to_country_map(); |
61 | 35 | create_dir_all(&openvpn_dir)?;
|
62 | 36 | delete_all_files_in_dir(&openvpn_dir)?;
|
63 |
| - |
64 |
| - let client = Client::new(); |
65 |
| - let relays: Vec<OpenVpnRelay> = client |
66 |
| - .get("https://api.mullvad.net/www/relays/openvpn/") |
67 |
| - .send()? |
68 |
| - .json()?; |
69 |
| - |
70 |
| - let mut config_choice = ConfigType::choose_one()?; |
71 |
| - let port = config_choice.generate_port(); |
72 |
| - |
73 |
| - let use_ips = dialoguer::Confirm::new() |
74 |
| - .with_prompt( |
75 |
| - "Use IP addresses instead of hostnames? (may be resistant to DNS blocking, but need to be synced more frequently)" |
76 |
| - ) |
77 |
| - .default(false) |
78 |
| - .interact()?; |
79 |
| - |
80 |
| - let use_bridges = dialoguer::Confirm::new() |
81 |
| - .with_prompt( |
82 |
| - "Connect via a bridge? (route over two separate servers, requires connecting on TCP port 443)" |
83 |
| - ) |
84 |
| - .default(false) |
85 |
| - .interact()?; |
86 |
| - |
87 |
| - let mut settings = self.get_default_openvpn_settings(); |
88 |
| - |
89 |
| - if use_bridges { |
90 |
| - if config_choice != ConfigType::Tcp443 { |
91 |
| - warn!("Overriding chosen protocol and port to TCP 443 due to use of bridge"); |
92 |
| - config_choice = ConfigType::Tcp443; |
93 |
| - } |
94 |
| - settings.push("socks-proxy 127.0.0.1 1080"); |
95 |
| - } |
96 |
| - |
97 |
| - match config_choice.get_protocol() { |
98 |
| - OpenVpnProtocol::UDP => { |
99 |
| - settings.push("proto udp"); |
100 |
| - settings.push("fast-io"); |
101 |
| - } |
102 |
| - OpenVpnProtocol::TCP => { |
103 |
| - settings.push("proto tcp"); |
104 |
| - } |
105 |
| - } |
106 |
| - |
107 |
| - // Group relays by country |
108 |
| - // Generate config file per relay given options |
109 |
| - // Naming: country_name-hostalias.ovpn |
110 |
| - let mut file_set: HashMap<String, Vec<String>> = HashMap::with_capacity(128); |
111 |
| - for relay in relays.into_iter().filter(|x| x.active) { |
112 |
| - let file_name = format!( |
113 |
| - "{}-{}.ovpn", |
114 |
| - relay.country_name.to_lowercase().replace(' ', "_"), |
115 |
| - relay.country_code |
116 |
| - ); |
117 |
| - |
118 |
| - let remote_string = if use_ips { |
119 |
| - format!( |
120 |
| - "remote {} {} # {}", |
121 |
| - relay.ipv4_addr_in, port, relay.hostname |
122 |
| - ) |
123 |
| - } else { |
124 |
| - format!("remote {}.mullvad.net {}", relay.hostname, port) |
125 |
| - }; |
126 |
| - |
127 |
| - file_set |
128 |
| - .entry(file_name) |
129 |
| - .or_insert_with(Vec::new) |
130 |
| - .push(remote_string); |
131 |
| - } |
132 |
| - |
133 |
| - let bridge_vec = if use_bridges { |
134 |
| - let bridges: Vec<OpenVpnRelay> = client |
135 |
| - .get("https://api.mullvad.net/www/relays/bridge/") |
136 |
| - .send()? |
137 |
| - .json()?; |
138 |
| - bridges |
139 |
| - .into_iter() |
140 |
| - .filter(|x| x.active) |
141 |
| - .map(|x| { |
142 |
| - format!( |
143 |
| - "route {} 255.255.255.255 net_gateway # {}", |
144 |
| - x.ipv4_addr_in, x.hostname |
145 |
| - ) |
146 |
| - }) |
147 |
| - .collect::<Vec<String>>() |
148 |
| - } else { |
149 |
| - Vec::new() |
150 |
| - }; |
151 |
| - |
152 |
| - for (file_name, mut remote_vec) in file_set.into_iter() { |
153 |
| - let mut file = File::create(&openvpn_dir.join(file_name))?; |
154 |
| - writeln!(file, "{}", settings.join("\n"))?; |
155 |
| - |
156 |
| - remote_vec.shuffle(&mut rand::thread_rng()); |
157 |
| - writeln!( |
158 |
| - file, |
159 |
| - "{}", |
160 |
| - remote_vec[0..remote_vec.len().min(64)].join("\n") |
161 |
| - )?; |
162 |
| - if remote_vec.len() > 1 { |
163 |
| - writeln!(file, "remote-random")?; |
164 |
| - } |
165 |
| - |
166 |
| - if !bridge_vec.is_empty() { |
167 |
| - writeln!(file, "{}", bridge_vec.join("\n"))?; |
168 |
| - } |
169 |
| - } |
170 |
| - |
171 |
| - // Write CA cert |
172 |
| - let ca = include_str!("mullvad_ca.crt"); |
173 |
| - { |
174 |
| - let file = File::create(openvpn_dir.join("mullvad_ca.crt")) |
175 |
| - .context("Could not create mullvad CA file")?; |
176 |
| - let mut write_buf = std::io::BufWriter::new(file); |
177 |
| - write!(write_buf, "{}", ca)?; |
| 37 | + for alias in self.server_aliases() { |
| 38 | + let url = format!("https://www.azirevpn.com/cfg/openvpn/generate?country={}&os=linux-cli&nat=1&port=random&protocol={}&tls=gcm&keys=0", alias, protocol); |
| 39 | + let file = reqwest::blocking::get(&url)?.bytes()?; |
| 40 | + |
| 41 | + let file_contents = std::str::from_utf8(&file)?; |
| 42 | + let file_contents = file_contents |
| 43 | + .split('\n') |
| 44 | + .filter(|&x| !(x.starts_with("up ") || x.starts_with("down "))) |
| 45 | + .collect::<Vec<&str>>() |
| 46 | + .join("\n"); |
| 47 | + |
| 48 | + let country = country_map |
| 49 | + .get(&alias[0..2]) |
| 50 | + .expect("Could not map country to name"); |
| 51 | + let filename = format!("{}-{}.ovpn", country, alias); |
| 52 | + debug!("Writing file: {}", filename); |
| 53 | + let mut outfile = |
| 54 | + File::create(openvpn_dir.join(filename.to_lowercase().replace(' ', "_")))?; |
| 55 | + write!(outfile, "{}", file_contents)?; |
178 | 56 | }
|
179 | 57 |
|
180 | 58 | // Write OpenVPN credentials file
|
181 | 59 | let (user, pass) = self.prompt_for_auth()?;
|
182 | 60 | let mut outfile = File::create(self.auth_file_path()?)?;
|
183 | 61 | write!(outfile, "{}\n{}", user, pass)?;
|
| 62 | + info!( |
| 63 | + "AzireVPN OpenVPN config written to {}", |
| 64 | + openvpn_dir.display() |
| 65 | + ); |
184 | 66 | Ok(())
|
185 | 67 | }
|
186 | 68 | }
|
187 |
| - |
188 |
| -#[derive(EnumIter, PartialEq)] |
189 |
| -enum ConfigType { |
190 |
| - DefaultUdp, |
191 |
| - Udp53, |
192 |
| - Tcp80, |
193 |
| - Tcp443, |
194 |
| -} |
195 |
| - |
196 |
| -impl ConfigType { |
197 |
| - fn get_protocol(&self) -> OpenVpnProtocol { |
198 |
| - match self { |
199 |
| - Self::DefaultUdp => OpenVpnProtocol::UDP, |
200 |
| - Self::Udp53 => OpenVpnProtocol::UDP, |
201 |
| - Self::Tcp80 => OpenVpnProtocol::TCP, |
202 |
| - Self::Tcp443 => OpenVpnProtocol::TCP, |
203 |
| - } |
204 |
| - } |
205 |
| - |
206 |
| - fn generate_port(&self) -> u16 { |
207 |
| - match self { |
208 |
| - Self::DefaultUdp => *[1300, 1301, 1302, 1194, 1195, 1196, 1197] |
209 |
| - .choose(&mut rand::thread_rng()) |
210 |
| - .expect("Could not choose default port"), |
211 |
| - Self::Udp53 => 53, |
212 |
| - Self::Tcp80 => 80, |
213 |
| - Self::Tcp443 => 443, |
214 |
| - } |
215 |
| - } |
216 |
| -} |
217 |
| - |
218 |
| -impl Display for ConfigType { |
219 |
| - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
220 |
| - let s = match self { |
221 |
| - Self::DefaultUdp => "Default (UDP)", |
222 |
| - Self::Udp53 => "UDP (Port 53)", |
223 |
| - Self::Tcp80 => "TCP (Port 80)", |
224 |
| - Self::Tcp443 => "TCP (Port 443)", |
225 |
| - }; |
226 |
| - write!(f, "{}", s) |
227 |
| - } |
228 |
| -} |
229 |
| - |
230 |
| -impl Default for ConfigType { |
231 |
| - fn default() -> Self { |
232 |
| - Self::DefaultUdp |
233 |
| - } |
234 |
| -} |
235 |
| - |
236 |
| -impl ConfigurationChoice for ConfigType { |
237 |
| - fn prompt() -> String { |
238 |
| - "Please choose your OpenVPN connection protocol and port".to_string() |
239 |
| - } |
240 |
| - |
241 |
| - fn variants() -> Vec<Self> { |
242 |
| - ConfigType::iter().collect() |
243 |
| - } |
244 |
| - |
245 |
| - fn description(&self) -> Option<String> { |
246 |
| - None |
247 |
| - } |
248 |
| -} |
249 |
| - |
250 |
| -// Note we ignore ipv6 addr here |
251 |
| -#[derive(Deserialize, Debug)] |
252 |
| -struct OpenVpnRelay { |
253 |
| - hostname: String, |
254 |
| - country_code: String, |
255 |
| - country_name: String, |
256 |
| - city_code: String, |
257 |
| - city_name: String, |
258 |
| - active: bool, |
259 |
| - owned: bool, |
260 |
| - provider: String, |
261 |
| - ipv4_addr_in: std::net::Ipv4Addr, |
262 |
| -} |
0 commit comments