Skip to content

Commit

Permalink
ネットワーク情報の取得ロジックの改良
Browse files Browse the repository at this point in the history
  • Loading branch information
shm11C3 committed Dec 31, 2024
1 parent 1238dbe commit 3852c26
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src-tauri/src/commands/hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ pub fn get_gpu_usage_history(
#[command]
#[specta::specta]
pub fn get_network_info() -> Result<Vec<NetworkInfo>, BackendError> {
system_info_service::get_network_info()
wmi_service::get_network_info().map_err(|_| BackendError::UnexpectedError)
}

///
Expand Down
2 changes: 2 additions & 0 deletions src-tauri/src/enums/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub enum BackendError {
GraphicInfoNotAvailable,
NetworkInfoNotAvailable,
NetworkUsageNotAvailable,
UnexpectedError,
// SystemError(String),
}

Expand All @@ -25,6 +26,7 @@ impl Serialize for BackendError {
BackendError::GraphicInfoNotAvailable => "graphicInfoNotAvailable",
BackendError::NetworkInfoNotAvailable => "networkInfoNotAvailable",
BackendError::NetworkUsageNotAvailable => "networkUsageNotAvailable",
BackendError::UnexpectedError => "unexpectedError",
// BackendError::SystemError(ref e) => e,
};
serializer.serialize_str(s)
Expand Down
36 changes: 1 addition & 35 deletions src-tauri/src/services/system_info_service.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use crate::enums;
use crate::enums::error::BackendError;
use crate::structs;
use crate::structs::hardware::NetworkInfo;
use crate::utils;
use crate::utils::formatter::SizeUnit;

use serde::{Deserialize, Serialize};
use specta::Type;
use std::sync::MutexGuard;
use sysinfo::{Disks, IpNetwork, NetworkData, Networks, System};
use sysinfo::{Disks, System};

#[derive(Serialize, Deserialize, Type)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -75,35 +73,3 @@ pub fn get_storage_info() -> Result<Vec<structs::hardware::StorageInfo>, String>

Ok(storage_info)
}

pub fn get_network_info() -> Result<Vec<NetworkInfo>, BackendError> {
let interfaces = Networks::new_with_refreshed_list();

if interfaces.is_empty() {
return Err(BackendError::NetworkInfoNotAvailable);
}

let network_info = interfaces
.values()
.map(|interface| {
let ip_networks = interface.ip_networks();
let mac = interface.mac_address();

// IPv4とIPv6を分ける
let (ipv4, ipv6): (Vec<_>, Vec<_>) = ip_networks
.iter()
.partition(|ip_network| ip_network.addr.is_ipv4());

Ok(NetworkInfo {
ipv4: ipv4
.into_iter()
.map(|ip: &IpNetwork| ip.addr.to_string())
.collect(),
ipv6: ipv6.into_iter().map(|ip| ip.addr.to_string()).collect(),
mac: mac.to_string(),
})
})
.collect::<Result<Vec<_>, _>>()?;

Ok(network_info)
}
90 changes: 88 additions & 2 deletions src-tauri/src/services/wmi_service.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use crate::structs::hardware::MemoryInfo;
use crate::structs::hardware::{MemoryInfo, NetworkInfo};
use crate::utils;
use crate::utils::formatter;
use crate::{log_error, log_info, log_internal};

use regex::Regex;
use serde::de::DeserializeOwned;
use serde::Deserialize;
use std::error::Error;
use std::net::IpAddr;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use wmi::{COMLibrary, WMIConnection};
Expand Down Expand Up @@ -105,8 +107,92 @@ pub async fn get_gpu_usage_by_device_and_engine(
})
}

#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct NetworkAdapterConfiguration {
description: Option<String>,
#[serde(rename = "MACAddress")]
mac_address: Option<String>,
#[serde(rename = "IPAddress")]
ip_address: Option<Vec<String>>,
#[serde(rename = "IPSubnet")]
ip_subnet: Option<Vec<String>>,
#[serde(rename = "DefaultIPGateway")]
default_ip_gateway: Option<Vec<String>>,
}

pub fn get_network_info() -> Result<Vec<NetworkInfo>, String> {
let results: Vec<NetworkAdapterConfiguration> = wmi_query_in_thread(
"SELECT Description, MACAddress, IPAddress, IPSubnet, DefaultIPGateway FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = TRUE".to_string(),
)?;

log_info!(
&format!("Network adapter configuration data: {:?}", results),
"get_network_info",
None::<&str>
);

let network_info: Result<Vec<NetworkInfo>, String> = results
.into_iter()
.map(|adapter| -> Result<NetworkInfo, String> {
// IPv4とIPv6を分ける
let (ipv4, ipv6): (Vec<_>, Vec<_>) = adapter
.ip_address
.unwrap_or_else(|| vec![])
.into_iter()
.filter_map(|ip| ip.parse::<IpAddr>().ok())
.partition(|ip| matches!(ip, IpAddr::V4(_))); // IPv4とIPv6に分ける

// IPv6を分割する
let (link_local_ipv6, normal_ipv6): (Vec<_>, Vec<_>) =
ipv6.into_iter().partition(|ip| match ip {
IpAddr::V6(v6) if utils::ip::is_unicast_link_local(v6) => true, // リンクローカル
_ => false,
});

// IPv4サブネットを取得
let ipv4_subnet: Vec<String> = adapter
.ip_subnet
.unwrap_or_else(|| vec![])
.into_iter()
.filter(|subnet| subnet.contains('.')) // IPv4形式を確認
.collect();

// IPv4とIPv6のデフォルトゲートウェイを分割する
let (default_ipv4_gateway, default_ipv6_gateway): (Vec<_>, Vec<_>) = adapter
.default_ip_gateway
.unwrap_or_else(|| vec![])
.into_iter()
.filter_map(|ip| ip.parse::<IpAddr>().ok())
.partition(|ip| matches!(ip, IpAddr::V4(_)));

Ok(NetworkInfo {
description: Some(adapter.description.unwrap_or_default()),
mac_address: Some(adapter.mac_address.unwrap_or_default()),
ip_v4: ipv4.into_iter().map(|ip| ip.to_string()).collect(),
ip_v6: normal_ipv6.into_iter().map(|ip| ip.to_string()).collect(),
link_local_ip_v6: link_local_ipv6
.into_iter()
.map(|ip| ip.to_string())
.collect(),
ip_subnet: ipv4_subnet,
default_ipv4_gateway: default_ipv4_gateway
.into_iter()
.map(|ip| ip.to_string())
.collect(),
default_ipv6_gateway: default_ipv6_gateway
.into_iter()
.map(|ip| ip.to_string())
.collect(),
})
})
.collect();

network_info
}

///
/// ## WMIから別スレッドで¥クエリ実行する(WMIを使用)
/// ## 別スレッドでWMIクエリ実行する
///
fn wmi_query_in_thread<T>(query: String) -> Result<Vec<T>, String>
where
Expand Down
11 changes: 8 additions & 3 deletions src-tauri/src/structs/hardware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,14 @@ pub struct StorageInfo {
#[derive(Serialize, Deserialize, Type)]
#[serde(rename_all = "camelCase")]
pub struct NetworkInfo {
pub ipv4: Vec<String>,
pub ipv6: Vec<String>,
pub mac: String,
pub description: Option<String>,
pub mac_address: Option<String>,
pub ip_v4: Vec<String>,
pub ip_v6: Vec<String>,
pub link_local_ip_v6: Vec<String>,
pub ip_subnet: Vec<String>,
pub default_ipv4_gateway: Vec<String>,
pub default_ipv6_gateway: Vec<String>,
}

#[derive(Serialize, Deserialize, Type)]
Expand Down
53 changes: 53 additions & 0 deletions src-tauri/src/tests/utils/ip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#[cfg(test)]
mod tests {
use crate::utils::ip::*;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

#[test]
fn test_link_local_ipv6() {
// リンクローカルアドレス
let ip = IpAddr::V6("fe80::1".parse::<Ipv6Addr>().unwrap());
assert_eq!(is_unicast_link_local(&ip), true);

// グローバルIPv6アドレス
let ip = IpAddr::V6("2400:4051::1".parse::<Ipv6Addr>().unwrap());
assert_eq!(is_unicast_link_local(&ip), true);

// IPv6マルチキャスト
let ip = IpAddr::V6("ff02::1".parse::<Ipv6Addr>().unwrap());
assert_eq!(is_unicast_link_local(&ip), false);

// 未指定アドレス
let ip = IpAddr::V6("::".parse::<Ipv6Addr>().unwrap());
assert_eq!(is_unicast_link_local(&ip), false);
}

#[test]
fn test_ipv4_not_link_local() {
// IPv4アドレス(リンクローカルではない)
let ip = IpAddr::V4("192.168.1.1".parse::<Ipv4Addr>().unwrap());
assert_eq!(is_unicast_link_local(&ip), false);

// IPv4アドレス(リンクローカルではない)
let ip = IpAddr::V4("127.0.0.1".parse::<Ipv4Addr>().unwrap());
assert_eq!(is_unicast_link_local(&ip), false);
}

#[test]
fn test_with_direct_ipv6() {
// 直接Ipv6Addr型でテスト
let ip = "fe80::1".parse::<Ipv6Addr>().unwrap();
assert_eq!(is_unicast_link_local(&ip), true);

let ip = "2400:4051::1".parse::<Ipv6Addr>().unwrap();
assert_eq!(is_unicast_link_local(&ip), false);
}

#[test]
fn test_with_invalid_format() {
// 無効なアドレス文字列はパースエラーを引き起こすためここではテストしないが、
// 型制約に沿ってテストする例
let ip = IpAddr::V4("255.255.255.255".parse::<Ipv4Addr>().unwrap());
assert_eq!(is_unicast_link_local(&ip), false);
}
}
2 changes: 2 additions & 0 deletions src-tauri/src/tests/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
pub mod color;
#[cfg(test)]
pub mod formatter;
#[cfg(test)]
pub mod ip;
11 changes: 11 additions & 0 deletions src-tauri/src/utils/ip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use std::net::IpAddr;

pub fn is_unicast_link_local<T>(ip: &T) -> bool
where
T: Into<IpAddr> + Clone,
{
match ip.clone().into() {
IpAddr::V6(v6) => v6.segments()[0] & 0xffc0 == 0xfe80,
_ => false,
}
}
1 change: 1 addition & 0 deletions src-tauri/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod color;
pub mod file;
pub mod formatter;
pub mod ip;
pub mod logger;
pub mod tauri;
4 changes: 2 additions & 2 deletions src/rspc/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ async setDecoration(isDecorated: boolean) : Promise<void> {

/** user-defined types **/

export type BackendError = "cpuInfoNotAvailable" | "storageInfoNotAvailable" | "memoryInfoNotAvailable" | "graphicInfoNotAvailable" | "networkInfoNotAvailable" | "networkUsageNotAvailable"
export type BackendError = "cpuInfoNotAvailable" | "storageInfoNotAvailable" | "memoryInfoNotAvailable" | "graphicInfoNotAvailable" | "networkInfoNotAvailable" | "networkUsageNotAvailable" | "unexpectedError"
/**
* - `file_id` : 画像ファイルID
* - `image_data` : 画像データのBase64文字列
Expand All @@ -324,7 +324,7 @@ export type HardwareType = "cpu" | "memory" | "gpu"
export type LineGraphColorStringSettings = { cpu: string; memory: string; gpu: string }
export type MemoryInfo = { size: string; clock: number; clockUnit: string; memoryCount: number; totalSlots: number; memoryType: string }
export type NameValue = { name: string; value: number }
export type NetworkInfo = { ipv4: string[]; ipv6: string[]; mac: string }
export type NetworkInfo = { description: string | null; macAddress: string | null; ipV4: string[]; ipV6: string[]; linkLocalIpV6: string[]; ipSubnet: string[]; defaultIpv4Gateway: string[]; defaultIpv6Gateway: string[] }
export type ProcessInfo = { pid: number; name: string; cpuUsage: number; memoryUsage: number }
export type SizeUnit = "B" | "KB" | "MB" | "GB"
export type StorageInfo = { name: string; size: number; sizeUnit: SizeUnit; free: number; freeUnit: SizeUnit; storageType: DiskKind; fileSystem: string }
Expand Down
13 changes: 9 additions & 4 deletions src/template/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,12 +238,17 @@ const NetworkInfo = () => {
<>
{networkInfo.map((network) => {
return (
<div key={network.mac} className="mt-4">
<div key={network.description} className="mt-4">
<InfoTable
data={{
ipv4: network.ipv4.join(", "),
ipv6: network.ipv6.join(", "),
mac: network.mac,
description: network.description ?? "",
mac_address: network.macAddress ?? "",
ipv4: network.ipV4.join(", "),
ipv6: network.ipV6.join(", "),
linkLocalIpV6: network.linkLocalIpV6.join(", "),
ip_subnet: network.ipSubnet.join(", "),
gatewayV4: network.defaultIpv4Gateway.join(", "),
gatewayV6: network.defaultIpv6Gateway.join(", "),
}}
/>
</div>
Expand Down

0 comments on commit 3852c26

Please sign in to comment.