Update Hosts in Rust中操作hosts文件
Rust启动
说真的,我挺不喜欢锈粉(Rustaceans)那股味道的,就好像我家两个原神粉(老婆和孩子)天天攻击我说魔兽世界是垃圾游戏一样,我,我,我打,我打扫卫生去……
但是,有时候,有时候,我会相信一切有尽头,相聚离开都有时候,没有什么会永垂不锈
前几天,我想搞一个各种语言GCD的玩意,看看各个语言的泛型约束方式和实现的比较,结果搞起Scala的时候,发现Scala 3现在用Coursier来管理依赖,搞半天下载不动。好嘛,那我就更新hosts文件。结果,有一些hosts下载下来domain对应的ip有好几个,能访问不能访问的都有,还要自己一个一个试,那也太坑了吧。。。
一怒之下,Rust,启动!
代码
Cargo大法好,Rust保平安。
工程文件
1[package]
2name = "updatehosts4r"
3version = "0.1.0"
4edition = "2024"
5
6[dependencies]
7reqwest = { version = "0.11", features = ["blocking"] }
8regex = "1.10"
9crossbeam = "0.8"
10chrono = "0.4"
不知道为什么cargo new
的时候,给我生成的edition
是2024年……
依赖的几个库,reqwest
是用来下载hosts文件的,regex
是模式匹配的,用于解析ping命令的输出,crossbeam
是用来并发操作的。chrono
是用来获取当前时间的。
程序功能
程序的功能很简单,就是下载hosts文件,然后解析出域名和对应的ip,然后ping这些ip,选择最快的ip,然后更新hosts文件。
graph TD A[下载hosts文件] --> B[解析出域名和对应的ip] B --> C[ping这些ip] C --> D[选择最快的ip] D --> E[更新hosts文件] E --> F[刷新DNS缓存]
稍微好玩一点就是有:下载hosts文件、ping每个域名对应的多个ip选择最快的一个。
下载文件
关于reqwest
(这个名字简直笑死我了……顺便吐槽下Rust糟糕的社区Crates.io奇葩的管理政策),我这里用的是blocking
模式,因为就一个文件,下载时间很短,没必要异步。>
1let resp = reqwest::blocking::get("https://cdn.jsdelivr.net/gh/ittuann/GitHub-IP-hosts@main/hosts")?;
这个调用的最后有一个?
,是一个语法糖,代表着如果get
如果返回了一个正确的值,则该值被提取了赋值给resp
,否则程序会中断执行下面的语句提前返回,把错误向上扩散。我们还是偏好显式地处理错误,如同下面地代码。
1 match get(*url) {
2 Ok(response) => {
3 if response.status().is_success() {
4 let content = response.text()?;
5 let mut file = File::create(TEMP_FILE_PATH)?;
6 file.write_all(content.as_bytes())?;
7 println!("Successfully downloaded from: {}", url);
8 return Ok(());
9 }
10 }
11 Err(e) => {
12 println!("Failed to download from {}: {}", url, e);
13 continue;
14 }
15 }
get
方法返回一个Result<Response>
,这个match
语句是处理Result
的典型用法,整挺好。
当然,这里地场景实在是太简单。
并发ping
并发ping的场景,我这里用的是crossbeam
的unbounded
通道。这个通道的容量是无限的,所以可以无限制地发送消息。示例代码如下:
1use crossbeam_channel::unbounded;
2
3// Create a channel of unbounded capacity.
4let (s, r) = unbounded();
5
6// Send a message into the channel.
7s.send("Hello, world!").unwrap();
8
9// Receive the message from the channel.
10assert_eq!(r.recv(), Ok("Hello, world!"));
在我们的程序中,主要考虑ping域名有时候需要等待挺久,就算是设置了超时1s,有的domain有四个ip,需要ping四次,所以需要并发。当一个线程完成对应某个域名的所有ip的计时后,把时间最短的结果发送到通道中,然后主进程从通道中接收结果,并更新字典记录。
1fn select_fastest_ips(hosts_map: &HashMap<String, Vec<String>>) -> HashMap<String, (String, u32)> {
2 let mut result = HashMap::new();
3 let (tx, rx) = unbounded();
4
5 for (domain, ips) in hosts_map {
6 let domain = domain.clone();
7 let ips = ips.clone();
8 let tx = tx.clone();
9
10 thread::spawn(move || {
11 let mut min_time = MAX_PING_TIME;
12 let mut fastest_ip = None;
13
14 for ip in ips {
15 match ping_ip(&ip) {
16 Ok(time) => {
17 let time_str = if time == MAX_PING_TIME {
18 "timeout".to_string()
19 } else {
20 format!("{} ms", time)
21 };
22 let domain_str = format!(" {} [{}]", domain, ip);
23 println!("{:<55} ==> {}", domain_str, time_str);
24 if time < min_time {
25 min_time = time;
26 fastest_ip = Some(ip);
27 }
28 },
29 Err(e) => {
30 println!("{} {} ==> {}", domain, ip, e);
31 continue;
32 }
33 }
34 }
35
36 if let Some(ip) = fastest_ip {
37 tx.send((domain, (ip, min_time))).unwrap();
38 }
39 });
40 }
41
42 drop(tx);
43
44 while let Ok((domain, ip_time)) = rx.recv() {
45 result.insert(domain, ip_time);
46 }
47
48 result
49}
这里,在每个线程中,需要使用的几个变量就是domain
,ips
,和用于通讯的tx
,我们采用了thread::spawn
来创建线程,并利用move
关键字把变量所有权转移给线程。也正是因为如此,我们在启动线程之前,需要把domain
、ips
和tx
克隆一份共线程内部使用。
并且,drop(tx)
会释放主线程的tx
,而每个线程中的clone
只会赋值一个通道的句柄,并且这个对句柄的应用会在线程结束时自动释放。最终这个结构实现了所有线程工作完成后,主进程才从通道中接收结果的阻塞等待才会结束。这个过程更好的用下面的程序演示:
这个程序需要两个依赖:
1cargo add crossbeam rand
源代码很简单。
1use crossbeam::channel::unbounded;
2use std::thread;
3use rand::Rng;
4use std::time::Duration;
5
6fn main() {
7 let (tx, rx) = unbounded();
8
9 // 创建3个工作线程
10 for i in 0..3 {
11 let tx = tx.clone();
12 thread::spawn(move || {
13 println!("Thread {} starting", i);
14 // sleep 1~10 s
15 let sleep_time = rand::rng().random_range(1..=10);
16 thread::sleep(Duration::from_secs(sleep_time));
17 tx.send(format!("Thread {} sleep {} s", i, sleep_time)).unwrap();
18 println!("Thread {} ended", i);
19 // 线程结束,tx的克隆在这里被丢弃
20 });
21 }
22
23 // 丢弃主线程的tx
24 drop(tx);
25
26 // 接收所有消息
27 for received in rx {
28 println!("Received: {}", received);
29 }
30}
一次运行可能像这样。
1cargo run
2# Thread 1 starting
3# Thread 0 starting
4# Thread 2 starting
5# Thread 2 ended
6# Received: Thread 2 sleep 6 s
7# Thread 1 ended
8# Received: Thread 1 sleep 8 s
9# Received: Thread 0 sleep 8 s
10# Thread 0 ended
但是朋友们请一定要把程序中的线程数改为300,睡眠秒数改为1..=100
,然后运行一下,真的比较有意思。想到这里,我感觉可以用Rust锈化一个传说中的Sleep排序,简直不要太美哦……
其它部分
其它对于文件的读取和写入、命令行的操作,意思不大,就不赘述了。
源代码
工程文件下面就是src
文件夹,里面就是源文件main.rs
。
1use std::fs::File;
2use std::io::{Write, BufRead, BufReader, Read};
3use std::collections::HashMap;
4use std::process::Command;
5use std::str::FromStr;
6use std::thread;
7use reqwest::blocking::get;
8use regex::Regex;
9use crossbeam::channel::unbounded;
10use chrono;
11
12// Constants
13const URLS: [&str; 4] = [
14 "https://cdn.jsdelivr.net/gh/ittuann/GitHub-IP-hosts@main/hosts",
15 "https://fastly.jsdelivr.net/gh/ittuann/GitHub-IP-hosts@main/hosts",
16 "https://testingcf.jsdelivr.net/gh/ittuann/GitHub-IP-hosts@main/hosts",
17 "https://gcore.jsdelivr.net/gh/ittuann/GitHub-IP-hosts@main/hosts",
18];
19
20const HOST_FILE_PATH: &str = "c:/windows/system32/drivers/etc/hosts";
21const TEMP_FILE_PATH: &str = "temp_hosts.txt";
22const TEMP_HOSTS_FILE_PATH: &str = "temp_hosts";
23const MAX_PING_TIME: u32 = std::u32::MAX;
24
25// File operations
26fn download_hosts_file() -> Result<(), Box<dyn std::error::Error>> {
27 for url in URLS.iter() {
28 println!("Trying to download from: {}", url);
29 match get(*url) {
30 Ok(response) => {
31 if response.status().is_success() {
32 let content = response.text()?;
33 let mut file = File::create(TEMP_FILE_PATH)?;
34 file.write_all(content.as_bytes())?;
35 println!("Successfully downloaded from: {}", url);
36 return Ok(());
37 }
38 }
39 Err(e) => {
40 println!("Failed to download from {}: {}", url, e);
41 continue;
42 }
43 }
44 }
45 Err("All download attempts failed".into())
46}
47
48fn parse_hosts_file() -> Result<HashMap<String, Vec<String>>, Box<dyn std::error::Error>> {
49 let file = File::open(TEMP_FILE_PATH)?;
50 let reader = BufReader::new(file);
51 let mut hosts_map: HashMap<String, Vec<String>> = HashMap::new();
52
53 for line in reader.lines() {
54 let line = line?;
55 let line = line.trim();
56
57 if line.is_empty() || line.starts_with('#') {
58 continue;
59 }
60
61 let parts: Vec<&str> = line.split_whitespace().collect();
62 if parts.len() >= 2 {
63 let ip = parts[0].to_string();
64 let domain = parts[1].to_string();
65
66 hosts_map.entry(domain)
67 .or_insert_with(Vec::new)
68 .push(ip);
69 }
70 }
71
72 Ok(hosts_map)
73}
74
75fn write_hosts_file(domain_ips: &HashMap<String, (String, u32)>) -> Result<(), Box<dyn std::error::Error>> {
76 let mut file = File::create(TEMP_HOSTS_FILE_PATH)?;
77
78 writeln!(file, "# Generated by updatehosts4r")?;
79 writeln!(file, "# Date: {}", chrono::Local::now().format("%Y-%m-%d %H:%M:%S"))?;
80 writeln!(file)?;
81
82 for (domain, (ip, time)) in domain_ips {
83 writeln!(file, "{} {} # ping: {}ms", ip, domain, time)?;
84 }
85
86 Ok(())
87}
88
89// Network operations
90fn ping_ip(ip: &str) -> Result<u32, Box<dyn std::error::Error>> {
91 let output = Command::new("ping")
92 .args(["-n", "4", "-w", "1000", ip])
93 .output()?;
94
95 let output_str = String::from_utf8_lossy(&output.stdout);
96
97 if !output.status.success() {
98 return Ok(MAX_PING_TIME);
99 }
100
101 let mut total_time = 0;
102 let mut count = 0;
103 let re = Regex::new(r"=(\d+)ms").unwrap();
104
105 for line in output_str.lines() {
106 if line.contains(ip) && line.contains("TTL=") {
107 if let Some(caps) = re.captures(line) {
108 if let Some(time_str) = caps.get(1) {
109 if let Ok(time) = u32::from_str(time_str.as_str()) {
110 total_time += time;
111 count += 1;
112 }
113 }
114 }
115 }
116 }
117
118 if count == 0 {
119 Ok(MAX_PING_TIME)
120 } else {
121 Ok(total_time / count)
122 }
123}
124
125fn select_fastest_ips(hosts_map: &HashMap<String, Vec<String>>) -> HashMap<String, (String, u32)> {
126 let mut result = HashMap::new();
127 let (tx, rx) = unbounded();
128
129 for (domain, ips) in hosts_map {
130 let domain = domain.clone();
131 let ips = ips.clone();
132 let tx = tx.clone();
133
134 thread::spawn(move || {
135 let mut min_time = MAX_PING_TIME;
136 let mut fastest_ip = None;
137
138 for ip in ips {
139 match ping_ip(&ip) {
140 Ok(time) => {
141 let time_str = if time == MAX_PING_TIME {
142 "timeout".to_string()
143 } else {
144 format!("{} ms", time)
145 };
146 let domain_str = format!(" {} [{}]", domain, ip);
147 println!("{:<55} ==> {}", domain_str, time_str);
148 if time < min_time {
149 min_time = time;
150 fastest_ip = Some(ip);
151 }
152 },
153 Err(e) => {
154 println!("{} {} ==> {}", domain, ip, e);
155 continue;
156 }
157 }
158 }
159
160 if let Some(ip) = fastest_ip {
161 tx.send((domain, (ip, min_time))).unwrap();
162 }
163 });
164 }
165
166 drop(tx);
167
168 while let Ok((domain, ip_time)) = rx.recv() {
169 result.insert(domain, ip_time);
170 }
171
172 result
173}
174
175// System operations
176fn copy_and_flush_dns() -> Result<(), Box<dyn std::error::Error>> {
177 let mut hosts_content = Vec::new();
178 File::open(TEMP_HOSTS_FILE_PATH)?.read_to_end(&mut hosts_content)?;
179
180 let mut system_hosts = File::create(HOST_FILE_PATH)?;
181 system_hosts.write_all(&hosts_content)?;
182
183 let output = Command::new("ipconfig")
184 .arg("/flushdns")
185 .output()?;
186
187 if !output.status.success() {
188 return Err("Failed to flush DNS cache".into());
189 }
190
191 Ok(())
192}
193
194// Main workflow
195fn run_select_fastest_ips() -> Result<(), Box<dyn std::error::Error>> {
196 let hosts_map = parse_hosts_file()?;
197 if hosts_map.is_empty() {
198 return Err("Hosts map is empty".into());
199 }
200
201 println!("Total domains: {}", hosts_map.len());
202
203 let fastest_ips = select_fastest_ips(&hosts_map);
204
205 println!("\nFastest IPs selected for {} domains", fastest_ips.len());
206 println!("\nResults (domain -> ip -> ping time):");
207 for (domain, (ip, time)) in &fastest_ips {
208 println!("{:>40} -> {:<15} -> {}ms", domain, ip, time);
209 }
210
211 println!("\nWriting results to hosts file...");
212 write_hosts_file(&fastest_ips)?;
213 println!("Done!");
214
215 Ok(())
216}
217
218// Main function
219fn main() {
220 if let Err(e) = download_hosts_file() {
221 println!("Error: {}", e);
222 return;
223 }
224
225 if let Err(e) = run_select_fastest_ips() {
226 println!("Error: {}", e);
227 return;
228 }
229
230 match copy_and_flush_dns() {
231 Ok(_) => println!("\nHosts file updated and DNS cache flushed successfully!"),
232 Err(e) => println!("\nWarning: Failed to update system hosts file: {}", e),
233 }
234
235 // Clean up temporary files
236 if let Err(e) = std::fs::remove_file(TEMP_FILE_PATH) {
237 println!("Error deleting temp file: {}", e);
238 } else {
239 println!("Temp file deleted");
240 }
241
242 if let Err(e) = std::fs::remove_file(TEMP_HOSTS_FILE_PATH) {
243 println!("Error deleting temp hosts file: {}", e);
244 } else {
245 println!("Temp hosts file deleted");
246 }
247
248 // show press any key to exit
249 println!("Press any key to exit...");
250 let mut input = String::new();
251 std::io::stdin().read_line(&mut input).unwrap();
252}
253
254// Tests
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn test_ping_ip_success() {
261 let result = ping_ip("39.156.70.46");
262 assert!(result.is_ok());
263 let avg_time = result.unwrap();
264 println!("Acutal Ping time: {}ms {}", avg_time, MAX_PING_TIME);
265 assert!(avg_time < MAX_PING_TIME, "Ping time should be less than MAX_PING_TIME");
266 assert!(avg_time > 0, "Ping time should be greater than 0");
267 }
268
269 #[test]
270 fn test_ping_ip_invalid() {
271 let result = ping_ip("256.256.256.256");
272 assert!(result.is_ok());
273 assert_eq!(result.unwrap(), MAX_PING_TIME);
274 }
275
276 #[test]
277 fn test_ping_ip_unreachable() {
278 let result = ping_ip("192.168.255.255");
279 assert!(result.is_ok());
280 assert_eq!(result.unwrap(), MAX_PING_TIME);
281 }
282
283 #[test]
284 fn test_select_fastest_ips() {
285 assert!(run_select_fastest_ips().is_ok());
286 }
287}
运行
1cargo run
2cargo build -release
3./target/release/updatehosts4r
当然,这个程序仅仅适用于Windows系统。linux下面更简单,但是我们WSL党根本不需要。后面可以把这个exe文件设置一个管理员权限运行(右键属性,设置如下图中箭头所示)。否则就要打开一个管理员权限的命令行运行上面的命令。如果没有权限,那就没办法更改hosts文件了。
下面是运行的输出,包括了下载的域名和对应的ip以及ping的时间。目前程序里设置了1s的超时。
Trying to download from: https://cdn.jsdelivr.net/gh/ittuann/GitHub-IP-hosts@main/hosts
Successfully downloaded from: https://cdn.jsdelivr.net/gh/ittuann/GitHub-IP-hosts@main/hosts
Total domains: 37
github.blog [192.0.66.2] ==> 81 ms
pipelines.actions.githubusercontent.com [13.107.42.16] ==> 119 ms
github.githubassets.com [185.199.108.154] ==> 265 ms
alive.github.com [140.82.113.25] ==> 289 ms
education.github.com [140.82.112.21] ==> 276 ms
copilot-proxy.githubusercontent.com [20.85.130.105] ==> 280 ms
github-cloud.s3.amazonaws.com [16.15.184.238] ==> 274 ms
codeload.github.com [140.82.114.10] ==> 273 ms
github.community [140.82.113.17] ==> 291 ms
origin-tracker.githubusercontent.com [140.82.113.21] ==> 274 ms
github.com [140.82.112.4] ==> 280 ms
central.github.com [140.82.112.22] ==> 274 ms
github.global.ssl.fastly.net [151.101.1.194] ==> 279 ms
github.io [185.199.108.153] ==> 281 ms
gist.github.com [140.82.112.3] ==> 282 ms
githubcopilot.com [140.82.112.18] ==> 275 ms
collector.github.com [140.82.112.21] ==> 279 ms
githubstatus.com [185.199.108.153] ==> 279 ms
api.github.com [140.82.112.6] ==> 280 ms
github-com.s3.amazonaws.com [16.15.193.138] ==> 286 ms
live.github.com [140.82.113.25] ==> 294 ms
github.githubassets.com [185.199.109.154] ==> 271 ms
education.github.com [140.82.113.21] ==> 269 ms
github-cloud.s3.amazonaws.com [16.15.192.141] ==> 283 ms
github.community [140.82.113.18] ==> 275 ms
github.com [140.82.114.3] ==> 282 ms
github.global.ssl.fastly.net [151.101.129.194] ==> 269 ms
central.github.com [140.82.113.22] ==> 281 ms
origin-tracker.githubusercontent.com [140.82.114.21] ==> 283 ms
github.io [185.199.109.153] ==> 267 ms
githubstatus.com [185.199.109.153] ==> 261 ms
githubcopilot.com [140.82.113.17] ==> 280 ms
collector.github.com [140.82.114.22] ==> 273 ms
gist.github.com [140.82.113.4] ==> 287 ms
api.github.com [140.82.113.6] ==> 285 ms
live.github.com [140.82.113.26] ==> 280 ms
github-com.s3.amazonaws.com [3.5.0.87] ==> 293 ms
avatars4.githubusercontent.com [185.199.108.133] ==> timeout
objects.githubusercontent.com [185.199.108.133] ==> timeout
avatars2.githubusercontent.com [185.199.108.133] ==> timeout
favicons.githubusercontent.com [185.199.108.133] ==> timeout
raw.githubusercontent.com [185.199.108.133] ==> timeout
cloud.githubusercontent.com [185.199.108.133] ==> timeout
user-images.githubusercontent.com [185.199.108.133] ==> timeout
avatars1.githubusercontent.com [185.199.108.133] ==> timeout
camo.githubusercontent.com [185.199.108.133] ==> timeout
media.githubusercontent.com [185.199.108.133] ==> timeout
avatars0.githubusercontent.com [185.199.108.133] ==> timeout
avatars5.githubusercontent.com [185.199.108.133] ==> timeout
avatars.githubusercontent.com [185.199.108.133] ==> timeout
avatars3.githubusercontent.com [185.199.108.133] ==> timeout
desktop.githubusercontent.com [185.199.108.133] ==> timeout
github.map.fastly.net [185.199.108.133] ==> timeout
github.githubassets.com [185.199.110.154] ==> 254 ms
github-com.s3.amazonaws.com [3.5.27.67] ==> 297 ms
github.global.ssl.fastly.net [151.101.193.194] ==> 241 ms
githubstatus.com [185.199.110.153] ==> 243 ms
github.io [185.199.110.153] ==> 236 ms
github.githubassets.com [185.199.111.154] ==> 278 ms
github-cloud.s3.amazonaws.com [3.5.27.149] ==> timeout
avatars4.githubusercontent.com [185.199.109.133] ==> timeout
camo.githubusercontent.com [185.199.109.133] ==> timeout
avatars3.githubusercontent.com [185.199.109.133] ==> timeout
avatars.githubusercontent.com [185.199.109.133] ==> timeout
github.map.fastly.net [185.199.109.133] ==> timeout
media.githubusercontent.com [185.199.109.133] ==> timeout
favicons.githubusercontent.com [185.199.109.133] ==> timeout
avatars5.githubusercontent.com [185.199.109.133] ==> timeout
cloud.githubusercontent.com [185.199.109.133] ==> timeout
raw.githubusercontent.com [185.199.109.133] ==> timeout
objects.githubusercontent.com [185.199.109.133] ==> timeout
avatars2.githubusercontent.com [185.199.109.133] ==> timeout
avatars0.githubusercontent.com [185.199.109.133] ==> timeout
avatars1.githubusercontent.com [185.199.109.133] ==> timeout
user-images.githubusercontent.com [185.199.109.133] ==> timeout
desktop.githubusercontent.com [185.199.109.133] ==> timeout
githubstatus.com [185.199.111.153] ==> 257 ms
github.io [185.199.111.153] ==> 258 ms
github.global.ssl.fastly.net [151.101.65.194] ==> 267 ms
avatars4.githubusercontent.com [185.199.110.133] ==> 251 ms
camo.githubusercontent.com [185.199.110.133] ==> 251 ms
github.map.fastly.net [185.199.110.133] ==> 249 ms
avatars3.githubusercontent.com [185.199.110.133] ==> 251 ms
media.githubusercontent.com [185.199.110.133] ==> 252 ms
avatars.githubusercontent.com [185.199.110.133] ==> 248 ms
raw.githubusercontent.com [185.199.110.133] ==> 248 ms
favicons.githubusercontent.com [185.199.110.133] ==> 249 ms
avatars2.githubusercontent.com [185.199.110.133] ==> 247 ms
avatars5.githubusercontent.com [185.199.110.133] ==> 250 ms
objects.githubusercontent.com [185.199.110.133] ==> 249 ms
cloud.githubusercontent.com [185.199.110.133] ==> 249 ms
avatars1.githubusercontent.com [185.199.110.133] ==> 254 ms
user-images.githubusercontent.com [185.199.110.133] ==> 256 ms
desktop.githubusercontent.com [185.199.110.133] ==> 257 ms
avatars0.githubusercontent.com [185.199.110.133] ==> 255 ms
github-com.s3.amazonaws.com [3.5.29.107] ==> timeout
github-cloud.s3.amazonaws.com [3.5.28.239] ==> timeout
avatars4.githubusercontent.com [185.199.111.133] ==> timeout
avatars.githubusercontent.com [185.199.111.133] ==> timeout
raw.githubusercontent.com [185.199.111.133] ==> timeout
avatars0.githubusercontent.com [185.199.111.133] ==> timeout
objects.githubusercontent.com [185.199.111.133] ==> timeout
avatars2.githubusercontent.com [185.199.111.133] ==> timeout
cloud.githubusercontent.com [185.199.111.133] ==> timeout
github.map.fastly.net [185.199.111.133] ==> timeout
camo.githubusercontent.com [185.199.111.133] ==> timeout
avatars3.githubusercontent.com [185.199.111.133] ==> timeout
user-images.githubusercontent.com [185.199.111.133] ==> timeout
media.githubusercontent.com [185.199.111.133] ==> timeout
avatars1.githubusercontent.com [185.199.111.133] ==> timeout
desktop.githubusercontent.com [185.199.111.133] ==> timeout
avatars5.githubusercontent.com [185.199.111.133] ==> timeout
favicons.githubusercontent.com [185.199.111.133] ==> timeout
Fastest IPs selected for 37 domains
Results (domain -> ip -> ping time):
avatars4.githubusercontent.com -> 185.199.110.133 -> 251ms
raw.githubusercontent.com -> 185.199.110.133 -> 248ms
github.global.ssl.fastly.net -> 151.101.193.194 -> 241ms
avatars0.githubusercontent.com -> 185.199.110.133 -> 255ms
origin-tracker.githubusercontent.com -> 140.82.113.21 -> 274ms
education.github.com -> 140.82.113.21 -> 269ms
live.github.com -> 140.82.113.26 -> 280ms
collector.github.com -> 140.82.114.22 -> 273ms
github.com -> 140.82.112.4 -> 280ms
github.map.fastly.net -> 185.199.110.133 -> 249ms
api.github.com -> 140.82.112.6 -> 280ms
avatars2.githubusercontent.com -> 185.199.110.133 -> 247ms
user-images.githubusercontent.com -> 185.199.110.133 -> 256ms
github-cloud.s3.amazonaws.com -> 16.15.184.238 -> 274ms
avatars1.githubusercontent.com -> 185.199.110.133 -> 254ms
github-com.s3.amazonaws.com -> 16.15.193.138 -> 286ms
copilot-proxy.githubusercontent.com -> 20.85.130.105 -> 280ms
objects.githubusercontent.com -> 185.199.110.133 -> 249ms
avatars3.githubusercontent.com -> 185.199.110.133 -> 251ms
camo.githubusercontent.com -> 185.199.110.133 -> 251ms
desktop.githubusercontent.com -> 185.199.110.133 -> 257ms
gist.github.com -> 140.82.112.3 -> 282ms
codeload.github.com -> 140.82.114.10 -> 273ms
github.githubassets.com -> 185.199.110.154 -> 254ms
github.io -> 185.199.110.153 -> 236ms
avatars.githubusercontent.com -> 185.199.110.133 -> 248ms
cloud.githubusercontent.com -> 185.199.110.133 -> 249ms
favicons.githubusercontent.com -> 185.199.110.133 -> 249ms
avatars5.githubusercontent.com -> 185.199.110.133 -> 250ms
central.github.com -> 140.82.112.22 -> 274ms
githubcopilot.com -> 140.82.112.18 -> 275ms
alive.github.com -> 140.82.113.25 -> 289ms
pipelines.actions.githubusercontent.com -> 13.107.42.16 -> 119ms
github.blog -> 192.0.66.2 -> 81ms
media.githubusercontent.com -> 185.199.110.133 -> 252ms
github.community -> 140.82.113.18 -> 275ms
githubstatus.com -> 185.199.110.153 -> 243ms
Writing results to hosts file...
Done!
Hosts file updated and DNS cache flushed successfully!
Temp file deleted
Temp hosts file deleted
Press any key to exit...
总结
反正最后我更新了hosts
文件,把Coursier
给装上了……
Rust,哦不,Scala,启动!
文章标签
|-->rust |-->hosts |-->reqwest |-->fs |-->cargo |-->crossbeam |-->github.com |-->Coursier
- 本站总访问量:次
- 本站总访客数:人
- 可通过邮件联系作者:Email大福
- 也可以访问技术博客:大福是小强
- 也可以在知乎搞抽象:知乎-大福
- Comments, requests, and/or opinions go to: Github Repository