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
use std::collections::HashMap;
use reqwest;
use serde::{Deserialize, Serialize};
use std::net::UdpSocket;
use crate::squire;

/// Function to retrieve the public IP address
///
/// # Returns
///
/// An `Option` containing the public IP address as a `String` if found, otherwise `None`
async fn public_ip_address() -> Option<String> {
    let ip_regex = squire::util::ip_regex();
    let mapping = squire::util::public_ip_mapping();

    for (url, expects_text) in mapping {
        match reqwest::get(&url).await {
            Ok(response) => {
                let extracted_ip = if expects_text {
                    response.text().await.unwrap_or_default().trim().to_string()
                } else {
                    response.json::<serde_json::Value>().await.ok()
                        .and_then(|json| json["origin"].as_str().map(str::to_string))
                        .unwrap_or_default()
                };

                if ip_regex.is_match(&extracted_ip) {
                    return Some(extracted_ip);
                }
            }
            Err(err) => {
                log::error!("Failed to fetch from {}: {}", &url, err);
                continue; // Move on to the next URL
            }
        }
    }

    None
}

/// Function to retrieve the private IP address
///
/// # Returns
///
/// An `Option` containing the private IP address as a `String` if found, otherwise `None`
fn private_ip_address() -> Option<String> {
    let socket = match UdpSocket::bind("0.0.0.0:0") {
        Ok(s) => s,
        Err(err) => {
            log::error!("Failed to bind to a socket: {}", err);
            return None;
        }
    };
    if socket.connect("8.8.8.8:80").is_err() {
        log::error!("Failed to connect to a socket");
        return None;
    }
    let local_addr = match socket.local_addr() {
        Ok(addr) => addr,
        Err(err) => {
            log::error!("Failed to get local IP address: {}", err);
            return None;
        }
    };
    Some(local_addr.ip().to_string())
}

/// Struct to hold the network information of the system
///
/// This struct holds the private and public IP addresses of the system.
///
/// # Fields
///
/// * `private_ip_address` - The private IP address of the system
/// * `public_ip_address` - The public IP address of the system
#[derive(Serialize, Deserialize, Debug)]
pub struct SystemInfoNetwork {
    private_ip_address_raw: String,
    public_ip_address_raw: String,
}

/// Function to get network information
///
/// This function retrieves the private and public IP addresses of the system.
pub async fn get_network_info() -> HashMap<&'static str, String> {
    let private_ip = private_ip_address().unwrap_or_default();
    let public_ip = public_ip_address().await.unwrap_or_default();
    HashMap::from([
        ("Private_IP_Address_raw", private_ip),
        ("Public_IP_Address_raw", public_ip),
    ])
}