aboutsummaryrefslogtreecommitdiff
path: root/src-tauri/src/platform_specific/windows.rs
blob: fc6aab5d665a0207c2e2068eb6d3b4d39c3b2fe5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/// Windows specific code
use anyhow::{anyhow, Result};
use std::net::Ipv4Addr;

#[cfg(target_os = "windows")]
use winreg::{enums::HKEY_LOCAL_MACHINE, RegKey};

use crate::repair_and_verify::check_is_valid_game_path;

/// Gets Titanfall2 install location on Origin
pub fn origin_install_location_detection() -> Result<String, anyhow::Error> {
    #[cfg(target_os = "windows")]
    {
        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
        match hklm.open_subkey("SOFTWARE\\Respawn\\Titanfall2") {
            Ok(tf) => {
                let game_path_str: String = tf.get_value("Install Dir")?;

                match check_is_valid_game_path(&game_path_str) {
                    Ok(()) => {
                        return Ok(game_path_str.to_string());
                    }
                    Err(err) => {
                        log::warn!("{err}");
                    }
                }
            }
            Err(err) => {
                log::warn!("{err}");
            }
        }
    }

    Err(anyhow!("No Origin / EA App install path found"))
}

/// Check whether the current device might be behind a CGNAT
pub async fn check_cgnat() -> Result<String, String> {
    // Use external service to grap IP
    let url = "https://api.ipify.org";
    let response = reqwest::get(url).await.unwrap().text().await.unwrap();

    // Check if valid IPv4 address and return early if not
    if response.parse::<Ipv4Addr>().is_err() {
        return Err(format!("Not valid IPv4 address: {}", response));
    }

    let hops_count = run_tracert(&response)?;
    Ok(format!("Counted {} hops to {}", hops_count, response))
}

/// Count number of hops in tracert output
fn count_hops(output: &str) -> usize {
    // Split the output into lines
    let lines: Vec<&str> = output.lines().collect();

    // Filter lines that appear to represent hops
    let hop_lines: Vec<&str> = lines
        .iter()
        .filter(|&line| line.contains("ms") || line.contains("*")) // TODO check if it contains just the `ms` surrounded by whitespace, otherwise it might falsely pick up some domain names as well
        .cloned()
        .collect();

    // Return the number of hops
    hop_lines.len()
}

/// Run `tracert`
fn run_tracert(target_ip: &str) -> Result<usize, String> {
    // Ensure valid IPv4 address to avoid prevent command injection
    assert!(target_ip.parse::<Ipv4Addr>().is_ok());

    // Execute the `tracert` command
    let output = match std::process::Command::new("tracert")
        .arg("-4") // Force IPv4
        .arg("-d") // Prevent resolving intermediate IP addresses
        .arg("-w") // Set timeout to 1 second
        .arg("1000")
        .arg("-h") // Set max hop count
        .arg("5")
        .arg(target_ip)
        .output()
    {
        Ok(res) => res,
        Err(err) => return Err(format!("Failed running tracert: {}", err)),
    };

    // Check if the command was successful
    if output.status.success() {
        // Convert the output to a string
        let stdout =
            std::str::from_utf8(&output.stdout).expect("Invalid UTF-8 sequence in command output");
        println!("{}", stdout);

        // Count the number of hops
        let hop_count = count_hops(stdout);
        Ok(hop_count)
    } else {
        let stderr = std::str::from_utf8(&output.stderr)
            .expect("Invalid UTF-8 sequence in command error output");
        println!("{}", stderr);
        Err(format!("Failed collecting tracert output: {}", stderr))
    }
}