1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
13
14use async_std::task;
15use ed25519_dalek::*;
16use futures::{channel::mpsc, Future};
17use lazy_static::lazy_static;
18use noise_protocol::U8Array;
19use noise_protocol::DH;
20use noise_rust_crypto::sensitive::Sensitive;
21use regex::Regex;
22use url::Host;
23use url::Url;
24
25#[allow(unused_imports)]
26use ng_repo::errors::*;
27use ng_repo::types::PubKey;
28use ng_repo::{log::*, types::PrivKey};
29
30use crate::types::*;
31use crate::NG_BOOTSTRAP_LOCAL_PATH;
32use crate::WS_PORT;
33
34#[doc(hidden)]
35#[cfg(target_arch = "wasm32")]
36pub fn spawn_and_log_error<F>(fut: F) -> task::JoinHandle<()>
37where
38 F: Future<Output = ResultSend<()>> + 'static,
39{
40 task::spawn_local(async move {
41 if let Err(e) = fut.await {
42 log_err!("EXCEPTION {}", e)
43 }
44 })
45}
46#[cfg(target_arch = "wasm32")]
47pub type ResultSend<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
48
49#[cfg(not(target_arch = "wasm32"))]
50pub type ResultSend<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
51
52#[doc(hidden)]
53#[cfg(not(target_arch = "wasm32"))]
54pub fn spawn_and_log_error<F>(fut: F) -> task::JoinHandle<()>
55where
56 F: Future<Output = ResultSend<()>> + Send + 'static,
57{
58 task::spawn(async move {
59 if let Err(e) = fut.await {
60 log_err!("{}", e)
61 }
62 })
63}
64
65#[cfg(target_arch = "wasm32")]
66#[cfg(debug_assertions)]
67const APP_PREFIX: &str = "http://localhost:14400";
68
69#[cfg(target_arch = "wasm32")]
70#[cfg(not(debug_assertions))]
71const APP_PREFIX: &str = "";
72
73pub fn decode_invitation_string(string: String) -> Option<Invitation> {
74 Invitation::try_from(string).ok()
75}
76
77lazy_static! {
78 #[doc(hidden)]
79 static ref RE_IPV6_WITH_PORT: Regex =
80 Regex::new(r"^\[([0-9a-fA-F:]{3,39})\](\:\d{1,5})?$").unwrap();
81}
82
83#[doc(hidden)]
84pub fn parse_ipv4_and_port_for(
85 string: String,
86 for_option: &str,
87 default_port: u16,
88) -> Result<(Ipv4Addr, u16), NgError> {
89 let parts: Vec<&str> = string.split(":").collect();
90 let ipv4 = parts[0].parse::<Ipv4Addr>().map_err(|_| {
91 NgError::ConfigError(format!(
92 "The <IPv4:PORT> value submitted for the {} option is invalid.",
93 for_option
94 ))
95 })?;
96
97 let port = if parts.len() > 1 {
98 match serde_json::from_str::<u16>(parts[1]) {
99 Err(_) => default_port,
100 Ok(p) => {
101 if p == 0 {
102 default_port
103 } else {
104 p
105 }
106 }
107 }
108 } else {
109 default_port
110 };
111 Ok((ipv4, port))
112}
113
114#[doc(hidden)]
115pub fn parse_ip_and_port_for(string: String, for_option: &str) -> Result<BindAddress, NgError> {
116 let bind = parse_ip_and_port_for_(string, for_option)?;
117 Ok(BindAddress {
118 ip: (&bind.0).into(),
119 port: bind.1,
120 })
121}
122
123fn parse_ip_and_port_for_(string: String, for_option: &str) -> Result<(IpAddr, u16), NgError> {
124 let c = RE_IPV6_WITH_PORT.captures(&string);
125 let ipv6;
126 let port;
127 if c.is_some() && c.as_ref().unwrap().get(1).is_some() {
128 let cap = c.unwrap();
129 let ipv6_str = cap.get(1).unwrap().as_str();
130 port = match cap.get(2) {
131 None => WS_PORT,
132 Some(p) => {
133 let mut chars = p.as_str().chars();
134 chars.next();
135 match serde_json::from_str::<u16>(chars.as_str()) {
136 Err(_) => WS_PORT,
137 Ok(p) => {
138 if p == 0 {
139 WS_PORT
140 } else {
141 p
142 }
143 }
144 }
145 }
146 };
147 let ipv6 = ipv6_str.parse::<Ipv6Addr>().map_err(|_| {
148 NgError::ConfigError(format!(
149 "The <[IPv6]:PORT> value submitted for the {} option is invalid.",
150 for_option
151 ))
152 })?;
153 Ok((IpAddr::V6(ipv6), port))
154 } else {
155 let ipv6_res = string.parse::<Ipv6Addr>();
157 if ipv6_res.is_err() {
158 parse_ipv4_and_port_for(string, for_option, WS_PORT)
161 .map(|ipv4| (IpAddr::V4(ipv4.0), ipv4.1))
162 } else {
163 ipv6 = ipv6_res.unwrap();
164 port = WS_PORT;
165 Ok((IpAddr::V6(ipv6), port))
166 }
167 }
168}
169
170pub fn check_is_local_url(bootstrap: &BrokerServerV0, location: &String) -> Option<String> {
171 if location.starts_with(NG_APP_URL) {
172 match &bootstrap.server_type {
173 BrokerServerTypeV0::Public(_) | BrokerServerTypeV0::BoxPublicDyn(_) => {
174 return Some(APP_NG_WS_URL.to_string());
175 }
176 _ => {}
177 }
178 } else if let BrokerServerTypeV0::Domain(domain) = &bootstrap.server_type {
179 let url = format!("https://{}", domain);
180 if location.starts_with(&url) {
181 return Some(url);
182 }
183 } else {
184 if location.starts_with(LOCAL_URLS[0])
186 || location.starts_with(LOCAL_URLS[1])
187 || location.starts_with(LOCAL_URLS[2])
188 {
189 if let BrokerServerTypeV0::Localhost(port) = bootstrap.server_type {
190 return Some(local_http_url(&port));
191 }
192 }
193 else if location.starts_with("http://") {
195 let url = Url::parse(location).unwrap();
196 match url.host() {
197 Some(Host::Ipv4(ip)) => {
198 if is_ipv4_private(&ip) {
199 let res = bootstrap.first_ipv4_http();
200 if res.is_some() {
201 return res;
202 }
203 }
204 }
205 Some(Host::Ipv6(ip)) => {
206 if is_ipv6_private(&ip) {
207 let res = bootstrap.first_ipv6_http();
208 if res.is_some() {
209 return res;
210 }
211 }
212 }
213 _ => {}
214 }
215 }
216 }
217 None
218}
219
220#[cfg(target_arch = "wasm32")]
221async fn retrieve_ng_bootstrap(location: &String) -> Option<LocalBootstrapInfo> {
222 let prefix = if APP_PREFIX == "" {
223 let url = Url::parse(location).unwrap();
224 url.origin().unicode_serialization()
225 } else {
226 APP_PREFIX.to_string()
227 };
228 let url = format!("{}{}", prefix, NG_BOOTSTRAP_LOCAL_PATH);
229 log_info!("url {}", url);
230 let resp = reqwest::get(url).await;
231 if resp.is_ok() {
233 let resp = resp.unwrap().json::<LocalBootstrapInfo>().await;
234 return if resp.is_ok() {
235 Some(resp.unwrap())
236 } else {
237 None
238 };
239 } else {
240 return None;
242 }
243}
244
245#[cfg(not(target_arch = "wasm32"))]
246pub async fn retrieve_ng_bootstrap(location: &String) -> Option<LocalBootstrapInfo> {
247 let url = Url::parse(location).unwrap();
248 let prefix = url.origin().unicode_serialization();
249 let url = format!("{}{}", prefix, NG_BOOTSTRAP_LOCAL_PATH);
250 log_info!("url {}", url);
251 let resp = reqwest::get(url).await;
252 if resp.is_ok() {
254 let resp = resp.unwrap().json::<LocalBootstrapInfo>().await;
255 return if resp.is_ok() {
256 Some(resp.unwrap())
257 } else {
258 None
259 };
260 } else {
261 return None;
263 }
264}
265
266#[cfg(target_arch = "wasm32")]
282pub async fn retrieve_local_url(location: String) -> Option<String> {
283 let info = retrieve_ng_bootstrap(&location).await;
284 if info.is_none() {
285 return None;
286 }
287 for bootstrap in info.unwrap().servers() {
288 let res = check_is_local_url(bootstrap, &location);
289 if res.is_some() {
290 return res;
291 }
292 }
293 None
294}
295
296#[cfg(target_arch = "wasm32")]
297pub async fn retrieve_local_bootstrap(
298 location_string: String,
299 invite_string: Option<String>,
300 must_be_public: bool,
301) -> Option<Invitation> {
302 let invite1: Option<Invitation> = if invite_string.is_some() {
303 let invitation: Result<Invitation, NgError> = invite_string.clone().unwrap().try_into();
304 invitation.ok()
305 } else {
306 None
307 };
308 log_debug!("{}", location_string);
309 log_debug!("invite_string {:?} invite1{:?}", invite_string, invite1);
310
311 let invite2: Option<Invitation> = {
312 let info = retrieve_ng_bootstrap(&location_string).await;
313 if info.is_none() {
314 None
315 } else {
316 let inv: Invitation = info.unwrap().into();
317 Some(inv)
318 }
319 };
320
321 let res = if invite1.is_none() {
322 invite2
323 } else if invite2.is_none() {
324 invite1
325 } else {
326 invite1.map(|i| i.intersects(invite2.unwrap()))
327 };
328
329 if res.is_some() {
330 for server in res.as_ref().unwrap().get_servers() {
331 if must_be_public && server.is_public_server()
332 || !must_be_public && check_is_local_url(server, &location_string).is_some()
333 {
334 return res;
335 }
336 }
337 return None;
338 }
339 res
340}
341
342pub fn sensitive_from_privkey(privkey: PrivKey) -> Sensitive<[u8; 32]> {
343 let mut bits: [u8; 32] = [0u8; 32];
345 bits.copy_from_slice(privkey.slice());
346 Sensitive::<[u8; 32]>::from_slice(&bits)
347}
348
349pub fn dh_privkey_from_sensitive(privkey: Sensitive<[u8; 32]>) -> PrivKey {
350 let mut bits: [u8; 32] = [0u8; 32];
352 bits.copy_from_slice(privkey.as_slice());
353 PrivKey::X25519PrivKey(bits)
354}
355
356pub type Sender<T> = mpsc::UnboundedSender<T>;
357pub type Receiver<T> = mpsc::UnboundedReceiver<T>;
358
359pub fn gen_dh_keys() -> (PrivKey, PubKey) {
360 let pri = noise_rust_crypto::X25519::genkey();
361 let publ = noise_rust_crypto::X25519::pubkey(&pri);
362
363 (dh_privkey_from_sensitive(pri), PubKey::X25519PubKey(publ))
364}
365
366pub struct Dual25519Keys {
367 pub x25519_priv: Sensitive<[u8; 32]>,
368 pub x25519_public: [u8; 32],
369 pub ed25519_priv: SecretKey,
370 pub ed25519_pub: PublicKey,
371}
372
373impl Dual25519Keys {
374 pub fn generate() -> Self {
375 let mut random = Sensitive::<[u8; 32]>::new();
376 getrandom::getrandom(&mut *random).expect("getrandom failed");
377
378 let ed25519_priv = SecretKey::from_bytes(&random.as_slice()).unwrap();
379 let exp: ExpandedSecretKey = (&ed25519_priv).into();
380 let mut exp_bytes = exp.to_bytes();
381 let ed25519_pub: PublicKey = (&ed25519_priv).into();
382 for byte in &mut exp_bytes[32..] {
383 *byte = 0;
384 }
385 let mut bits = Sensitive::<[u8; 32]>::from_slice(&exp_bytes[0..32]);
386 bits[0] &= 248;
387 bits[31] &= 127;
388 bits[31] |= 64;
389
390 let x25519_public = noise_rust_crypto::X25519::pubkey(&bits);
391
392 Self {
393 x25519_priv: bits,
394 x25519_public,
395 ed25519_priv,
396 ed25519_pub,
397 }
398 }
399}
400
401pub fn get_domain_without_port(domain: &String) -> String {
402 let parts: Vec<&str> = domain.split(':').collect();
403 parts[0].to_string()
404}
405
406pub fn get_domain_without_port_443(domain: &str) -> &str {
407 let parts: Vec<&str> = domain.split(':').collect();
408 if parts.len() > 1 && parts[1] == "443" {
409 return parts[0];
410 }
411 domain
412}
413
414pub fn is_public_ipv4(ip: &Ipv4Addr) -> bool {
415 return is_ipv4_global(ip);
417}
418
419pub fn is_public_ipv6(ip: &Ipv6Addr) -> bool {
420 return is_ipv6_global(ip);
422}
423
424pub fn is_public_ip(ip: &IpAddr) -> bool {
425 match ip {
426 IpAddr::V4(v4) => is_public_ipv4(v4),
427 IpAddr::V6(v6) => is_public_ipv6(v6),
428 }
429}
430
431pub fn is_private_ip(ip: &IpAddr) -> bool {
432 match ip {
433 IpAddr::V4(v4) => is_ipv4_private(v4),
434 IpAddr::V6(v6) => is_ipv6_private(v6),
435 }
436}
437
438#[must_use]
439#[inline]
440pub const fn is_ipv4_shared(addr: &Ipv4Addr) -> bool {
441 addr.octets()[0] == 100 && (addr.octets()[1] & 0b1100_0000 == 0b0100_0000)
442}
443
444#[must_use]
445#[inline]
446pub const fn is_ipv4_benchmarking(addr: &Ipv4Addr) -> bool {
447 addr.octets()[0] == 198 && (addr.octets()[1] & 0xfe) == 18
448}
449
450#[must_use]
451#[inline]
452pub const fn is_ipv4_reserved(addr: &Ipv4Addr) -> bool {
453 addr.octets()[0] & 240 == 240 && !addr.is_broadcast()
454}
455
456#[must_use]
457#[inline]
458pub const fn is_ipv4_private(addr: &Ipv4Addr) -> bool {
459 addr.is_private() || addr.is_link_local()
460}
461
462#[must_use]
463#[inline]
464pub const fn is_ipv4_global(addr: &Ipv4Addr) -> bool {
465 !(addr.octets()[0] == 0 || addr.is_private()
467 || is_ipv4_shared(addr)
468 || addr.is_loopback()
469 || addr.is_link_local()
470 ||(addr.octets()[0] == 192 && addr.octets()[1] == 0 && addr.octets()[2] == 0)
472 || addr.is_documentation()
473 || is_ipv4_benchmarking(addr)
474 || is_ipv4_reserved(addr)
475 || addr.is_broadcast())
476}
477
478#[must_use]
479#[inline]
480pub const fn is_ipv6_unique_local(addr: &Ipv6Addr) -> bool {
481 (addr.segments()[0] & 0xfe00) == 0xfc00
482}
483
484#[must_use]
485#[inline]
486pub const fn is_ipv6_unicast_link_local(addr: &Ipv6Addr) -> bool {
487 (addr.segments()[0] & 0xffc0) == 0xfe80
488}
489
490#[must_use]
491#[inline]
492pub const fn is_ipv6_documentation(addr: &Ipv6Addr) -> bool {
493 (addr.segments()[0] == 0x2001) && (addr.segments()[1] == 0xdb8)
494}
495
496#[must_use]
497#[inline]
498pub const fn is_ipv6_private(addr: &Ipv6Addr) -> bool {
499 is_ipv6_unique_local(addr)
500}
501
502#[must_use]
503#[inline]
504pub const fn is_ipv6_global(addr: &Ipv6Addr) -> bool {
505 !(addr.is_unspecified()
506 || addr.is_loopback()
507 || matches!(addr.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
509 || matches!(addr.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
511 || matches!(addr.segments(), [0x100, 0, 0, 0, _, _, _, _])
513 || (matches!(addr.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
515 && !(
516 u128::from_be_bytes(addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
518 || u128::from_be_bytes(addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
520 || matches!(addr.segments(), [0x2001, 3, _, _, _, _, _, _])
522 || matches!(addr.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
524 || matches!(addr.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F)
526 ))
527 || is_ipv6_documentation(addr)
528 || is_ipv6_unique_local(addr)
529 || is_ipv6_unicast_link_local(addr))
530}