mirror of
https://github.com/therealaleph/MasterHttpRelayVPN-RUST.git
synced 2026-05-18 07:44:47 +03:00
chor: update docs and read batch size and header checking from conf
This commit is contained in:
@@ -183,7 +183,9 @@ You can enable dynamic IP discovery by setting fetch_ips_from_api to true in con
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"fetch_ips_from_api": true,
|
"fetch_ips_from_api": true,
|
||||||
"max_ips_to_scan": 100
|
"max_ips_to_scan": 100,
|
||||||
|
"scan_batch_size":100,
|
||||||
|
"google_ip_validation": true // check whether ips belongs to frontend sites of google or not
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -470,7 +472,9 @@ Firefox cert store خودش را جدا دارد؛ installer تلاش میک
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"fetch_ips_from_api": true,
|
"fetch_ips_from_api": true,
|
||||||
"max_ips_to_scan": 100
|
"max_ips_to_scan": 100,
|
||||||
|
"scan_batch_size":100,
|
||||||
|
"google_ip_validation": true // برسی هدر های بازگشته از ایپی برای برسی هدر ها و تشخیص کاربردی بودن ایپی
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+11
-3
@@ -132,7 +132,9 @@ struct FormState {
|
|||||||
/// Whether the floating SNI editor window is open.
|
/// Whether the floating SNI editor window is open.
|
||||||
sni_editor_open: bool,
|
sni_editor_open: bool,
|
||||||
fetch_ips_from_api: bool,
|
fetch_ips_from_api: bool,
|
||||||
max_ips_to_scan: usize,
|
max_ips_to_scan: usize,
|
||||||
|
scan_batch_size:usize,
|
||||||
|
google_ip_validation: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@@ -180,6 +182,8 @@ fn load_form() -> FormState {
|
|||||||
sni_editor_open: false,
|
sni_editor_open: false,
|
||||||
fetch_ips_from_api:c.fetch_ips_from_api,
|
fetch_ips_from_api:c.fetch_ips_from_api,
|
||||||
max_ips_to_scan:c.max_ips_to_scan,
|
max_ips_to_scan:c.max_ips_to_scan,
|
||||||
|
google_ip_validation: c.google_ip_validation,
|
||||||
|
scan_batch_size:c.scan_batch_size
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
FormState {
|
FormState {
|
||||||
@@ -200,6 +204,8 @@ fn load_form() -> FormState {
|
|||||||
sni_editor_open: false,
|
sni_editor_open: false,
|
||||||
fetch_ips_from_api:false,
|
fetch_ips_from_api:false,
|
||||||
max_ips_to_scan:100,
|
max_ips_to_scan:100,
|
||||||
|
google_ip_validation:true,
|
||||||
|
scan_batch_size:500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,8 +310,10 @@ impl FormState {
|
|||||||
// on an empty pool.
|
// on an empty pool.
|
||||||
if active.is_empty() { None } else { Some(active) }
|
if active.is_empty() { None } else { Some(active) }
|
||||||
},
|
},
|
||||||
fetch_ips_from_api:false,
|
fetch_ips_from_api:self.fetch_ips_from_api,
|
||||||
max_ips_to_scan: 100,
|
max_ips_to_scan: self.max_ips_to_scan,
|
||||||
|
google_ip_validation:self.google_ip_validation,
|
||||||
|
scan_batch_size:self.scan_batch_size
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -85,11 +85,18 @@ pub struct Config {
|
|||||||
|
|
||||||
#[serde(default = "default_max_ips_to_scan")]
|
#[serde(default = "default_max_ips_to_scan")]
|
||||||
pub max_ips_to_scan: usize,
|
pub max_ips_to_scan: usize,
|
||||||
|
|
||||||
|
#[serde(default = "default_scan_batch_size")]
|
||||||
|
pub scan_batch_size:usize,
|
||||||
|
|
||||||
|
#[serde(default = "default_google_ip_validation")]
|
||||||
|
pub google_ip_validation: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_fetch_ips_from_api() -> bool { false }
|
fn default_fetch_ips_from_api() -> bool { false }
|
||||||
fn default_max_ips_to_scan() -> usize { 100 }
|
fn default_max_ips_to_scan() -> usize { 100 }
|
||||||
|
fn default_scan_batch_size() -> usize {500}
|
||||||
|
fn default_google_ip_validation() -> bool {true}
|
||||||
|
|
||||||
fn default_google_ip() -> String {
|
fn default_google_ip() -> String {
|
||||||
"216.239.38.120".into()
|
"216.239.38.120".into()
|
||||||
|
|||||||
+138
-84
@@ -2,6 +2,7 @@ use std::net::SocketAddr;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio_rustls::rustls::client::danger::{
|
use tokio_rustls::rustls::client::danger::{
|
||||||
@@ -10,7 +11,6 @@ use tokio_rustls::rustls::client::danger::{
|
|||||||
use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
|
use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
|
||||||
use tokio_rustls::rustls::{ClientConfig, DigitallySignedStruct, SignatureScheme};
|
use tokio_rustls::rustls::{ClientConfig, DigitallySignedStruct, SignatureScheme};
|
||||||
use tokio_rustls::TlsConnector;
|
use tokio_rustls::TlsConnector;
|
||||||
use rand::seq::SliceRandom;
|
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
|
||||||
@@ -67,9 +67,14 @@ struct Result_ {
|
|||||||
|
|
||||||
pub async fn run(config: &Config) -> bool {
|
pub async fn run(config: &Config) -> bool {
|
||||||
let ips = fetch_google_ips(config).await;
|
let ips = fetch_google_ips(config).await;
|
||||||
|
let google_ip_validation = config.google_ip_validation;
|
||||||
let sni = config.front_domain.clone();
|
let sni = config.front_domain.clone();
|
||||||
println!("Scanning {} Google frontend IPs (SNI={}, timeout={}s)...", ips.len(), sni, PROBE_TIMEOUT.as_secs());
|
println!(
|
||||||
|
"Scanning {} Google frontend IPs (SNI={}, timeout={}s)...",
|
||||||
|
ips.len(),
|
||||||
|
sni,
|
||||||
|
PROBE_TIMEOUT.as_secs()
|
||||||
|
);
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
let tls_cfg = ClientConfig::builder()
|
let tls_cfg = ClientConfig::builder()
|
||||||
@@ -86,8 +91,8 @@ pub async fn run(config: &Config) -> bool {
|
|||||||
let sem = sem.clone();
|
let sem = sem.clone();
|
||||||
let ip = ip.to_string();
|
let ip = ip.to_string();
|
||||||
tasks.push(tokio::spawn(async move {
|
tasks.push(tokio::spawn(async move {
|
||||||
let _permit = sem.acquire().await.ok();
|
let _permit: Option<tokio::sync::SemaphorePermit<'_>> = sem.acquire().await.ok();
|
||||||
probe(&ip, &sni, connector).await
|
probe(&ip, &sni, connector,google_ip_validation).await
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +140,14 @@ async fn fetch_google_ips(config: &Config) -> Vec<String> {
|
|||||||
return CANDIDATE_IPS.iter().map(|s| s.to_string()).collect();
|
return CANDIDATE_IPS.iter().map(|s| s.to_string()).collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
match fetch_and_validate_google_ips(&config.front_domain, config.max_ips_to_scan).await {
|
match fetch_and_validate_google_ips(
|
||||||
|
&config.front_domain,
|
||||||
|
config.max_ips_to_scan,
|
||||||
|
config.scan_batch_size,
|
||||||
|
config.google_ip_validation
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
Ok(ips) if !ips.is_empty() => {
|
Ok(ips) if !ips.is_empty() => {
|
||||||
tracing::info!("✓ Validated {} working IPs from goog.json", ips.len());
|
tracing::info!("✓ Validated {} working IPs from goog.json", ips.len());
|
||||||
ips
|
ips
|
||||||
@@ -145,15 +157,26 @@ async fn fetch_google_ips(config: &Config) -> Vec<String> {
|
|||||||
CANDIDATE_IPS.iter().map(|s| s.to_string()).collect()
|
CANDIDATE_IPS.iter().map(|s| s.to_string()).collect()
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!("Failed to fetch/validate Google IPs: {}, using static fallback", e);
|
tracing::warn!(
|
||||||
|
"Failed to fetch/validate Google IPs: {}, using static fallback",
|
||||||
|
e
|
||||||
|
);
|
||||||
CANDIDATE_IPS.iter().map(|s| s.to_string()).collect()
|
CANDIDATE_IPS.iter().map(|s| s.to_string()).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_and_validate_google_ips(sni: &str, max_ips: usize) -> Result<Vec<String>, Box<dyn std::error::Error>> {
|
async fn fetch_and_validate_google_ips(
|
||||||
|
sni: &str,
|
||||||
|
max_ips: usize,
|
||||||
|
batch_size: usize,
|
||||||
|
google_ip_validation: bool
|
||||||
|
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
|
||||||
let famous_ips = resolve_famous_domains().await;
|
let famous_ips = resolve_famous_domains().await;
|
||||||
tracing::info!("Resolved {} IPs from famous Google domains", famous_ips.len());
|
tracing::info!(
|
||||||
|
"Resolved {} IPs from famous Google domains",
|
||||||
|
famous_ips.len()
|
||||||
|
);
|
||||||
|
|
||||||
let cidrs = fetch_google_cidrs().await?;
|
let cidrs = fetch_google_cidrs().await?;
|
||||||
tracing::info!("Fetched {} CIDR blocks from goog.json", cidrs.len());
|
tracing::info!("Fetched {} CIDR blocks from goog.json", cidrs.len());
|
||||||
@@ -174,13 +197,17 @@ async fn fetch_and_validate_google_ips(sni: &str, max_ips: usize) -> Result<Vec<
|
|||||||
}
|
}
|
||||||
let other_ips_len = &other_ips.len();
|
let other_ips_len = &other_ips.len();
|
||||||
|
|
||||||
tracing::info!("Extracted {} priority IPs and {} other IPs", priority_ips.len(), other_ips.len());
|
tracing::info!(
|
||||||
|
"Extracted {} priority IPs and {} other IPs",
|
||||||
|
priority_ips.len(),
|
||||||
|
other_ips.len()
|
||||||
|
);
|
||||||
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let priority_ips_len = &priority_ips.len();
|
let priority_ips_len = &priority_ips.len();
|
||||||
|
|
||||||
priority_ips.shuffle(&mut rng);
|
priority_ips.shuffle(&mut rng);
|
||||||
|
|
||||||
other_ips.shuffle(&mut rng);
|
other_ips.shuffle(&mut rng);
|
||||||
|
|
||||||
let mut candidate_ips = Vec::new();
|
let mut candidate_ips = Vec::new();
|
||||||
@@ -194,23 +221,29 @@ async fn fetch_and_validate_google_ips(sni: &str, max_ips: usize) -> Result<Vec<
|
|||||||
return Err("No valid IPs extracted from CIDRs".into());
|
return Err("No valid IPs extracted from CIDRs".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!("Selected {} IPs to test (from {} total), testing in batches...", candidate_ips.len(), priority_ips_len + other_ips_len);
|
tracing::info!(
|
||||||
|
"Selected {} IPs to test (from {} total), testing in batches...",
|
||||||
|
candidate_ips.len(),
|
||||||
|
priority_ips_len + other_ips_len
|
||||||
|
);
|
||||||
|
|
||||||
let batch_size = 50;
|
|
||||||
let mut working_ips = Vec::new();
|
let mut working_ips = Vec::new();
|
||||||
|
|
||||||
for (i, chunk) in candidate_ips.chunks(batch_size).enumerate() {
|
for (i, chunk) in candidate_ips.chunks(batch_size).enumerate() {
|
||||||
tracing::debug!("Testing batch {} ({} IPs)...", i + 1, chunk.len());
|
tracing::debug!("Testing batch {} ({} IPs)...", i + 1, chunk.len());
|
||||||
let batch_working = validate_ips(chunk, sni).await;
|
let batch_working = validate_ips(chunk, sni,google_ip_validation).await;
|
||||||
working_ips.extend(batch_working);
|
working_ips.extend(batch_working);
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!("Found {} working IPs from {} tested", working_ips.len(), candidate_ips.len());
|
tracing::info!(
|
||||||
|
"Found {} working IPs from {} tested",
|
||||||
|
working_ips.len(),
|
||||||
|
candidate_ips.len()
|
||||||
|
);
|
||||||
|
|
||||||
Ok(working_ips)
|
Ok(working_ips)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn resolve_famous_domains() -> Vec<String> {
|
async fn resolve_famous_domains() -> Vec<String> {
|
||||||
let mut ips = Vec::new();
|
let mut ips = Vec::new();
|
||||||
for domain in FAMOUS_GOOGLE_DOMAINS {
|
for domain in FAMOUS_GOOGLE_DOMAINS {
|
||||||
@@ -288,8 +321,9 @@ fn ip_to_u32(ip: &str) -> Option<u32> {
|
|||||||
async fn fetch_google_cidrs() -> Result<Vec<String>, Box<dyn std::error::Error>> {
|
async fn fetch_google_cidrs() -> Result<Vec<String>, Box<dyn std::error::Error>> {
|
||||||
let stream = tokio::time::timeout(
|
let stream = tokio::time::timeout(
|
||||||
Duration::from_secs(10),
|
Duration::from_secs(10),
|
||||||
TcpStream::connect("www.gstatic.com:443")
|
TcpStream::connect("www.gstatic.com:443"),
|
||||||
).await??;
|
)
|
||||||
|
.await??;
|
||||||
|
|
||||||
let tls_cfg = ClientConfig::builder()
|
let tls_cfg = ClientConfig::builder()
|
||||||
.dangerous()
|
.dangerous()
|
||||||
@@ -297,11 +331,12 @@ async fn fetch_google_cidrs() -> Result<Vec<String>, Box<dyn std::error::Error>>
|
|||||||
.with_no_client_auth();
|
.with_no_client_auth();
|
||||||
let connector = TlsConnector::from(Arc::new(tls_cfg));
|
let connector = TlsConnector::from(Arc::new(tls_cfg));
|
||||||
let server_name = ServerName::try_from("www.gstatic.com".to_string())?;
|
let server_name = ServerName::try_from("www.gstatic.com".to_string())?;
|
||||||
|
|
||||||
let mut tls_stream = tokio::time::timeout(
|
let mut tls_stream = tokio::time::timeout(
|
||||||
Duration::from_secs(10),
|
Duration::from_secs(10),
|
||||||
connector.connect(server_name, stream)
|
connector.connect(server_name, stream),
|
||||||
).await??;
|
)
|
||||||
|
.await??;
|
||||||
|
|
||||||
let request = "GET /ipranges/goog.json HTTP/1.1\r\n\
|
let request = "GET /ipranges/goog.json HTTP/1.1\r\n\
|
||||||
Host: www.gstatic.com\r\n\
|
Host: www.gstatic.com\r\n\
|
||||||
@@ -311,19 +346,17 @@ async fn fetch_google_cidrs() -> Result<Vec<String>, Box<dyn std::error::Error>>
|
|||||||
tls_stream.flush().await?;
|
tls_stream.flush().await?;
|
||||||
|
|
||||||
let mut response = Vec::new();
|
let mut response = Vec::new();
|
||||||
tokio::time::timeout(
|
tokio::time::timeout(Duration::from_secs(15), async {
|
||||||
Duration::from_secs(15),
|
let mut buf = [0u8; 4096];
|
||||||
async {
|
loop {
|
||||||
let mut buf = [0u8; 4096];
|
match tls_stream.read(&mut buf).await {
|
||||||
loop {
|
Ok(0) => break,
|
||||||
match tls_stream.read(&mut buf).await {
|
Ok(n) => response.extend_from_slice(&buf[..n]),
|
||||||
Ok(0) => break,
|
Err(_) => break,
|
||||||
Ok(n) => response.extend_from_slice(&buf[..n]),
|
|
||||||
Err(_) => break,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
).await?;
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
let response_str = String::from_utf8_lossy(&response);
|
let response_str = String::from_utf8_lossy(&response);
|
||||||
let body = response_str
|
let body = response_str
|
||||||
@@ -332,9 +365,7 @@ async fn fetch_google_cidrs() -> Result<Vec<String>, Box<dyn std::error::Error>>
|
|||||||
.ok_or("No HTTP body found")?;
|
.ok_or("No HTTP body found")?;
|
||||||
|
|
||||||
let json: serde_json::Value = serde_json::from_str(body)?;
|
let json: serde_json::Value = serde_json::from_str(body)?;
|
||||||
let prefixes = json["prefixes"]
|
let prefixes = json["prefixes"].as_array().ok_or("No prefixes array")?;
|
||||||
.as_array()
|
|
||||||
.ok_or("No prefixes array")?;
|
|
||||||
|
|
||||||
let mut cidrs = Vec::new();
|
let mut cidrs = Vec::new();
|
||||||
for prefix in prefixes {
|
for prefix in prefixes {
|
||||||
@@ -351,33 +382,34 @@ fn cidr_to_ips(cidr: &str) -> Vec<String> {
|
|||||||
if parts.len() != 2 {
|
if parts.len() != 2 {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let base_ip = parts[0];
|
let base_ip = parts[0];
|
||||||
let prefix_len: u8 = match parts[1].parse() {
|
let prefix_len: u8 = match parts[1].parse() {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
Err(_) => return Vec::new(),
|
Err(_) => return Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let octets: Vec<&str> = base_ip.split('.').collect();
|
let octets: Vec<&str> = base_ip.split('.').collect();
|
||||||
if octets.len() != 4 {
|
if octets.len() != 4 {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
let o: Vec<u8> = match octets.iter().map(|s| s.parse()).collect() {
|
let o: Vec<u8> = match octets.iter().map(|s| s.parse()).collect() {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(_) => return Vec::new(),
|
Err(_) => return Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let base = ((o[0] as u32) << 24) | ((o[1] as u32) << 16) | ((o[2] as u32) << 8) | (o[3] as u32);
|
let base = ((o[0] as u32) << 24) | ((o[1] as u32) << 16) | ((o[2] as u32) << 8) | (o[3] as u32);
|
||||||
let host_bits = 32 - prefix_len;
|
let host_bits = 32 - prefix_len;
|
||||||
let num_hosts = 1u32 << host_bits;
|
let num_hosts = 1u32 << host_bits;
|
||||||
|
|
||||||
let limit = num_hosts.min(256);
|
let limit = num_hosts.min(256);
|
||||||
|
|
||||||
(1..limit - 1)
|
(1..limit - 1)
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let ip = base + i;
|
let ip = base + i;
|
||||||
format!("{}.{}.{}.{}",
|
format!(
|
||||||
|
"{}.{}.{}.{}",
|
||||||
(ip >> 24) & 0xFF,
|
(ip >> 24) & 0xFF,
|
||||||
(ip >> 16) & 0xFF,
|
(ip >> 16) & 0xFF,
|
||||||
(ip >> 8) & 0xFF,
|
(ip >> 8) & 0xFF,
|
||||||
@@ -387,39 +419,44 @@ fn cidr_to_ips(cidr: &str) -> Vec<String> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn validate_ips(ips: &[String], sni: &str) -> Vec<String> {
|
async fn validate_ips(ips: &[String], sni: &str, google_ip_validation: bool) -> Vec<String> {
|
||||||
let tls_cfg = ClientConfig::builder()
|
let tls_cfg = ClientConfig::builder()
|
||||||
.dangerous()
|
.dangerous()
|
||||||
.with_custom_certificate_verifier(Arc::new(NoVerify))
|
.with_custom_certificate_verifier(Arc::new(NoVerify))
|
||||||
.with_no_client_auth();
|
.with_no_client_auth();
|
||||||
let connector = TlsConnector::from(Arc::new(tls_cfg));
|
let connector = TlsConnector::from(Arc::new(tls_cfg));
|
||||||
|
|
||||||
let sem = Arc::new(tokio::sync::Semaphore::new(CONCURRENCY));
|
let sem = Arc::new(tokio::sync::Semaphore::new(CONCURRENCY));
|
||||||
let mut tasks = Vec::new();
|
let mut tasks = Vec::new();
|
||||||
|
|
||||||
for ip in ips {
|
for ip in ips {
|
||||||
let ip = ip.clone();
|
let ip = ip.clone();
|
||||||
let sni = sni.to_string();
|
let sni = sni.to_string();
|
||||||
let connector = connector.clone();
|
let connector = connector.clone();
|
||||||
let sem = sem.clone();
|
let sem = sem.clone();
|
||||||
|
|
||||||
tasks.push(tokio::spawn(async move {
|
tasks.push(tokio::spawn(async move {
|
||||||
let _permit = sem.acquire().await.ok();
|
let _permit = sem.acquire().await.ok();
|
||||||
let result = quick_probe(&ip, &sni, connector).await;
|
let result = quick_probe(&ip, &sni, connector, google_ip_validation).await;
|
||||||
(ip, result)
|
(ip, result)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut working = Vec::new();
|
let mut working = Vec::new();
|
||||||
for task in tasks {
|
for task in tasks {
|
||||||
if let Ok((ip, true)) = task.await {
|
if let Ok((ip, true)) = task.await {
|
||||||
working.push(ip);
|
working.push(ip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
working
|
working
|
||||||
}
|
}
|
||||||
async fn quick_probe(ip: &str, sni: &str, connector: TlsConnector) -> bool {
|
async fn quick_probe(
|
||||||
|
ip: &str,
|
||||||
|
sni: &str,
|
||||||
|
connector: TlsConnector,
|
||||||
|
google_ip_validation: bool,
|
||||||
|
) -> bool {
|
||||||
let addr: SocketAddr = match format!("{}:443", ip).parse() {
|
let addr: SocketAddr = match format!("{}:443", ip).parse() {
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
@@ -435,12 +472,18 @@ async fn quick_probe(ip: &str, sni: &str, connector: TlsConnector) -> bool {
|
|||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tls = match tokio::time::timeout(Duration::from_secs(2), connector.connect(server_name, tcp)).await {
|
let mut tls =
|
||||||
Ok(Ok(t)) => t,
|
match tokio::time::timeout(Duration::from_secs(2), connector.connect(server_name, tcp))
|
||||||
_ => return false,
|
.await
|
||||||
};
|
{
|
||||||
|
Ok(Ok(t)) => t,
|
||||||
|
_ => return false,
|
||||||
|
};
|
||||||
|
|
||||||
let req = format!("HEAD / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n", sni);
|
let req = format!(
|
||||||
|
"HEAD / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
|
||||||
|
sni
|
||||||
|
);
|
||||||
if tls.write_all(req.as_bytes()).await.is_err() {
|
if tls.write_all(req.as_bytes()).await.is_err() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -450,22 +493,29 @@ async fn quick_probe(ip: &str, sni: &str, connector: TlsConnector) -> bool {
|
|||||||
match tokio::time::timeout(Duration::from_secs(2), tls.read(&mut buf)).await {
|
match tokio::time::timeout(Duration::from_secs(2), tls.read(&mut buf)).await {
|
||||||
Ok(Ok(n)) if n > 0 => {
|
Ok(Ok(n)) if n > 0 => {
|
||||||
let response = String::from_utf8_lossy(&buf[..n]);
|
let response = String::from_utf8_lossy(&buf[..n]);
|
||||||
|
|
||||||
if !response.starts_with("HTTP/") {
|
if !response.starts_with("HTTP/") {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let lower = response.to_lowercase();
|
if google_ip_validation {
|
||||||
lower.contains("server: gws") ||
|
let lower = response.to_lowercase();
|
||||||
lower.contains("x-google-") ||
|
return lower.contains("server: gws")
|
||||||
lower.contains("alt-svc: h3=")
|
|| lower.contains("x-google-")
|
||||||
|
|| lower.contains("alt-svc: h3=");
|
||||||
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn probe(
|
||||||
async fn probe(ip: &str, sni: &str, connector: TlsConnector) -> Result_ {
|
ip: &str,
|
||||||
|
sni: &str,
|
||||||
|
connector: TlsConnector,
|
||||||
|
google_ip_validation: bool,
|
||||||
|
) -> Result_ {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let addr: SocketAddr = match format!("{}:443", ip).parse() {
|
let addr: SocketAddr = match format!("{}:443", ip).parse() {
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
@@ -508,23 +558,24 @@ async fn probe(ip: &str, sni: &str, connector: TlsConnector) -> Result_ {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tls = match tokio::time::timeout(PROBE_TIMEOUT, connector.connect(server_name, tcp)).await {
|
let mut tls =
|
||||||
Ok(Ok(t)) => t,
|
match tokio::time::timeout(PROBE_TIMEOUT, connector.connect(server_name, tcp)).await {
|
||||||
Ok(Err(e)) => {
|
Ok(Ok(t)) => t,
|
||||||
return Result_ {
|
Ok(Err(e)) => {
|
||||||
ip: ip.into(),
|
return Result_ {
|
||||||
latency_ms: None,
|
ip: ip.into(),
|
||||||
error: Some(format!("tls: {}", e)),
|
latency_ms: None,
|
||||||
|
error: Some(format!("tls: {}", e)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
Err(_) => {
|
||||||
Err(_) => {
|
return Result_ {
|
||||||
return Result_ {
|
ip: ip.into(),
|
||||||
ip: ip.into(),
|
latency_ms: None,
|
||||||
latency_ms: None,
|
error: Some("tls timeout".into()),
|
||||||
error: Some("tls timeout".into()),
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
let req = format!(
|
let req = format!(
|
||||||
"HEAD / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
|
"HEAD / HTTP/1.1\r\nHost: {}\r\nConnection: close\r\n\r\n",
|
||||||
@@ -543,7 +594,7 @@ async fn probe(ip: &str, sni: &str, connector: TlsConnector) -> Result_ {
|
|||||||
match tokio::time::timeout(PROBE_TIMEOUT, tls.read(&mut buf)).await {
|
match tokio::time::timeout(PROBE_TIMEOUT, tls.read(&mut buf)).await {
|
||||||
Ok(Ok(n)) if n > 0 => {
|
Ok(Ok(n)) if n > 0 => {
|
||||||
let response = String::from_utf8_lossy(&buf[..n]);
|
let response = String::from_utf8_lossy(&buf[..n]);
|
||||||
|
|
||||||
if !response.starts_with("HTTP/") {
|
if !response.starts_with("HTTP/") {
|
||||||
return Result_ {
|
return Result_ {
|
||||||
ip: ip.into(),
|
ip: ip.into(),
|
||||||
@@ -551,12 +602,15 @@ async fn probe(ip: &str, sni: &str, connector: TlsConnector) -> Result_ {
|
|||||||
error: Some("bad reply".into()),
|
error: Some("bad reply".into()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let lower = response.to_lowercase();
|
let lower = response.to_lowercase();
|
||||||
let is_google = lower.contains("server: gws") ||
|
let mut is_google = true;
|
||||||
lower.contains("x-google-") ||
|
if google_ip_validation {
|
||||||
lower.contains("alt-svc: h3=");
|
is_google = lower.contains("server: gws")
|
||||||
|
|| lower.contains("x-google-")
|
||||||
|
|| lower.contains("alt-svc: h3=");
|
||||||
|
}
|
||||||
|
|
||||||
if is_google {
|
if is_google {
|
||||||
let elapsed = start.elapsed().as_millis();
|
let elapsed = start.elapsed().as_millis();
|
||||||
Result_ {
|
Result_ {
|
||||||
|
|||||||
Reference in New Issue
Block a user