Skip to content

Commit 4fcfbfe

Browse files
committed
add simpleobfs & latency server.
1 parent 2c8d2b4 commit 4fcfbfe

File tree

13 files changed

+978
-498
lines changed

13 files changed

+978
-498
lines changed

Cargo.lock

Lines changed: 602 additions & 474 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ async-trait = "0.1"
2222
base64 = "0.21.2"
2323
bitvec = "1"
2424
bloomfilter = "1.0.9"
25-
boring = "2.1.0"
26-
boring-sys = "2.1.0"
25+
boring = "4.2.0"
26+
boring-sys = "4.2.0"
2727
byte_string = "1.0"
2828
bytes = "1"
2929
chacha20poly1305 = "0.10"
@@ -49,7 +49,7 @@ sha2 = "0.10.6"
4949
socket2 = {version = "0.4.7", features = ["all"]}
5050
spin = "0.9.6"
5151
tokio = {version = "1.26", features = ["net", "io-util", "macros", "sync"]}
52-
tokio-boring = "2.1.5"
52+
tokio-boring = "4.2.0"
5353
tokio-tungstenite = {version = "0.20", features = ["stream", "handshake"], default-features = false}
5454
tokio-util = {version = "0.7", features = ["codec", "net"]}
5555
toml = "0.5"

src/api/api.proto

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,32 @@ message Stat {
1414
int64 value = 2;
1515
}
1616

17+
enum LatencyType{
18+
TCPING=0;
19+
REALPING=1;
20+
}
21+
22+
message GetLatencyRequest{
23+
optional string outbound_name =1;
24+
string test_url=2;
25+
}
26+
27+
message GetLatencyResponse{
28+
map<string,int64> latency_res=2;
29+
}
30+
1731
message GetStatsResponse { Stat stat = 1; }
1832

1933
// message QueryStatsRequest {
2034
// string pattern = 1;
2135
// bool reset = 2;
2236
// }
2337

24-
message QueryStatsResponse { repeated Stat stat = 1; }
25-
2638
service StatsService {
2739
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
28-
//rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {}
2940
}
41+
service LatencyService {
42+
rpc GetLatency(GetLatencyRequest) returns (GetLatencyResponse) {}
43+
}
44+
3045

src/api/mod.rs

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1-
use crate::api::v2ray_rust_api::{GetStatsRequest, GetStatsResponse};
1+
use crate::api::v2ray_rust_api::{
2+
GetLatencyRequest, GetLatencyResponse, GetStatsRequest, GetStatsResponse,
3+
};
24
use crate::config::COUNTER_MAP;
5+
use crate::proxy::{Address, ChainStreamBuilder};
36

7+
use std::collections::HashMap;
8+
use std::str::FromStr;
49
use std::sync::atomic::Ordering::Relaxed;
10+
use std::sync::Arc;
11+
use std::time::{Duration, Instant};
512

13+
use futures_util::future::join_all;
14+
use tokio::io::AsyncWriteExt;
15+
use tokio::time::timeout;
616
use tonic::{Request, Response, Status};
717

818
pub mod v2ray_rust_api {
919
tonic::include_proto!("v2ray.core.app.stats.command");
1020
}
21+
22+
use v2ray_rust_api::latency_service_server::{LatencyService, LatencyServiceServer};
1123
use v2ray_rust_api::stats_service_server::{StatsService, StatsServiceServer};
1224

1325
#[derive(Default)]
@@ -45,3 +57,79 @@ impl StatsService for ApiServer {
4557
}))
4658
}
4759
}
60+
61+
pub struct ApiLatencyServer {
62+
inner_map: Arc<HashMap<String, ChainStreamBuilder>>,
63+
}
64+
impl ApiLatencyServer {
65+
pub(crate) fn new_server(
66+
inner_map: Arc<HashMap<String, ChainStreamBuilder>>,
67+
) -> LatencyServiceServer<Self> {
68+
LatencyServiceServer::new(Self { inner_map })
69+
}
70+
}
71+
#[tonic::async_trait]
72+
impl LatencyService for ApiLatencyServer {
73+
async fn get_latency(
74+
&self,
75+
request: Request<GetLatencyRequest>,
76+
) -> Result<Response<GetLatencyResponse>, Status> {
77+
if let Some(req) = &request.get_ref().outbound_name {
78+
if let Some(b) = self.inner_map.get(req) {
79+
let start = Instant::now();
80+
let stream = b
81+
.build_tcp(
82+
Address::from_str(request.get_ref().test_url.as_str()).map_err(|_| {
83+
Status::new(tonic::Code::InvalidArgument, "test_url is invalid")
84+
})?,
85+
)
86+
.await;
87+
let duration = start.elapsed();
88+
let mut latency_res = HashMap::new();
89+
if stream.is_err() {
90+
latency_res.insert(req.clone(), -1i64);
91+
return Ok(Response::new(GetLatencyResponse { latency_res }));
92+
} else {
93+
latency_res.insert(req.clone(), duration.as_millis() as i64);
94+
return Ok(Response::new(GetLatencyResponse { latency_res }));
95+
}
96+
} else {
97+
return Err(Status::new(tonic::Code::InvalidArgument, "name is invalid"));
98+
}
99+
} else {
100+
let mut vec_fut = Vec::new();
101+
let test_url = request.get_ref().test_url.as_str();
102+
let addr = Address::from_str(test_url)
103+
.map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "invalid test url"))?;
104+
for (name, _) in self.inner_map.iter() {
105+
let name = name.clone();
106+
let addr = addr.clone();
107+
vec_fut.push(async move {
108+
let b = self.inner_map.get(&name).unwrap();
109+
let start = Instant::now();
110+
let timeout_stream = timeout(Duration::from_secs(5), async move {
111+
b.build_tcp(addr).await?.write_u128(u128::MAX).await
112+
})
113+
.await;
114+
let duration = start.elapsed();
115+
if timeout_stream.is_err() {
116+
return Ok::<(String, i64), std::io::Error>((name, -1i64));
117+
} else {
118+
let stream = timeout_stream?;
119+
if stream.is_err() {
120+
return Ok((name, -1i64));
121+
}
122+
return Ok((name, duration.as_millis() as i64));
123+
}
124+
});
125+
}
126+
let vec_res = join_all(vec_fut).await;
127+
let mut latency_res = HashMap::new();
128+
for v in vec_res.into_iter() {
129+
let (k, v) = v?;
130+
latency_res.insert(k, v);
131+
}
132+
return Ok(Response::new(GetLatencyResponse { latency_res }));
133+
}
134+
}
135+
}

src/config/geoip.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// This file is generated by rust-protobuf 3.2.0. Do not edit
2-
// .proto file is parsed by protoc 3.21.12
1+
// This file is generated by rust-protobuf 3.3.0. Do not edit
2+
// .proto file is parsed by protoc 25.1
33
// @generated
44

55
// https://github.com/rust-lang/rust-clippy/issues/702
@@ -23,10 +23,10 @@
2323
2424
/// Generated files are compatible only with the same version
2525
/// of protobuf runtime.
26-
const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
26+
const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_3_0;
2727

28-
#[derive(PartialEq,Clone,Default,Debug)]
2928
// @@protoc_insertion_point(message:CIDR)
29+
#[derive(PartialEq,Clone,Default,Debug)]
3030
pub struct CIDR {
3131
// message fields
3232
// @@protoc_insertion_point(field:CIDR.ip)
@@ -177,8 +177,8 @@ impl ::protobuf::reflect::ProtobufValue for CIDR {
177177
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
178178
}
179179

180-
#[derive(PartialEq,Clone,Default,Debug)]
181180
// @@protoc_insertion_point(message:GeoIP)
181+
#[derive(PartialEq,Clone,Default,Debug)]
182182
pub struct GeoIP {
183183
// message fields
184184
// @@protoc_insertion_point(field:GeoIP.country_code)
@@ -330,8 +330,8 @@ impl ::protobuf::reflect::ProtobufValue for GeoIP {
330330
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
331331
}
332332

333-
#[derive(PartialEq,Clone,Default,Debug)]
334333
// @@protoc_insertion_point(message:GeoIPList)
334+
#[derive(PartialEq,Clone,Default,Debug)]
335335
pub struct GeoIPList {
336336
// message fields
337337
// @@protoc_insertion_point(field:GeoIPList.entry)

src/config/geosite.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// This file is generated by rust-protobuf 3.2.0. Do not edit
2-
// .proto file is parsed by protoc 3.21.12
1+
// This file is generated by rust-protobuf 3.3.0. Do not edit
2+
// .proto file is parsed by protoc 25.1
33
// @generated
44

55
// https://github.com/rust-lang/rust-clippy/issues/702
@@ -23,10 +23,10 @@
2323
2424
/// Generated files are compatible only with the same version
2525
/// of protobuf runtime.
26-
const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
26+
const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_3_0;
2727

28-
#[derive(PartialEq,Clone,Default,Debug)]
2928
// @@protoc_insertion_point(message:Domain)
29+
#[derive(PartialEq,Clone,Default,Debug)]
3030
pub struct Domain {
3131
// message fields
3232
// @@protoc_insertion_point(field:Domain.type)
@@ -204,8 +204,8 @@ impl ::protobuf::reflect::ProtobufValue for Domain {
204204

205205
/// Nested message and enums of message `Domain`
206206
pub mod domain {
207-
#[derive(PartialEq,Clone,Default,Debug)]
208207
// @@protoc_insertion_point(message:Domain.Attribute)
208+
#[derive(PartialEq,Clone,Default,Debug)]
209209
pub struct Attribute {
210210
// message fields
211211
// @@protoc_insertion_point(field:Domain.Attribute.key)
@@ -486,6 +486,16 @@ pub mod domain {
486486
}
487487
}
488488

489+
fn from_str(str: &str) -> ::std::option::Option<Type> {
490+
match str {
491+
"Plain" => ::std::option::Option::Some(Type::Plain),
492+
"Regex" => ::std::option::Option::Some(Type::Regex),
493+
"Domain" => ::std::option::Option::Some(Type::Domain),
494+
"Full" => ::std::option::Option::Some(Type::Full),
495+
_ => ::std::option::Option::None
496+
}
497+
}
498+
489499
const VALUES: &'static [Type] = &[
490500
Type::Plain,
491501
Type::Regex,
@@ -519,8 +529,8 @@ pub mod domain {
519529
}
520530
}
521531

522-
#[derive(PartialEq,Clone,Default,Debug)]
523532
// @@protoc_insertion_point(message:SiteGroup)
533+
#[derive(PartialEq,Clone,Default,Debug)]
524534
pub struct SiteGroup {
525535
// message fields
526536
// @@protoc_insertion_point(field:SiteGroup.tag)
@@ -672,8 +682,8 @@ impl ::protobuf::reflect::ProtobufValue for SiteGroup {
672682
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
673683
}
674684

675-
#[derive(PartialEq,Clone,Default,Debug)]
676685
// @@protoc_insertion_point(message:SiteGroupList)
686+
#[derive(PartialEq,Clone,Default,Debug)]
677687
pub struct SiteGroupList {
678688
// message fields
679689
// @@protoc_insertion_point(field:SiteGroupList.site_group)

src/config/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ struct Http2Config {
187187
path: http::uri::PathAndQuery,
188188
}
189189

190+
#[derive(Deserialize, Clone)]
191+
struct SimpleObfsConfig {
192+
tag: String,
193+
#[serde(deserialize_with = "from_str_to_address")]
194+
host: Address,
195+
}
196+
190197
#[derive(Deserialize, Clone)]
191198
struct GrpcConfig {
192199
tag: String,
@@ -225,6 +232,8 @@ pub struct Config {
225232
#[serde(default)]
226233
blackhole: Vec<BlackHoleConfig>,
227234
#[serde(default)]
235+
simpleobfs: Vec<SimpleObfsConfig>,
236+
#[serde(default)]
228237
dokodemo: Vec<DokodemoDoor>,
229238
#[serde(default)]
230239
domain_routing_rules: Vec<DomainRoutingRules>,
@@ -259,6 +268,7 @@ impl std::ops::Index<(ProtocolType, usize)> for Config {
259268
ProtocolType::H2 => &self.h2[index.1],
260269
ProtocolType::Grpc => &self.grpc[index.1],
261270
ProtocolType::Blackhole => &self.blackhole[index.1],
271+
ProtocolType::SimpleObfs => &self.simpleobfs[index.1],
262272
}
263273
}
264274
}
@@ -291,6 +301,7 @@ impl Config {
291301
insert_config_map!(self.h2, config_map);
292302
insert_config_map!(self.grpc, config_map);
293303
insert_config_map!(self.blackhole, config_map);
304+
insert_config_map!(self.simpleobfs, config_map);
294305
let mut inner_map = HashMap::new();
295306
for out in self.outbounds.iter() {
296307
let mut addrs = Vec::new();

src/config/server_builder.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::api::ApiServer;
1+
use crate::api::{ApiLatencyServer, ApiServer};
22
use crate::common::net::{relay, relay_with_atomic_counter};
33
use crate::config::{DokodemoDoor, Inbounds, Router};
44
use crate::proxy::dokodemo_door::build_dokodemo_door_listener;
@@ -91,13 +91,16 @@ impl ConfigServerBuilder {
9191
}
9292
let inner_map = (&self.inner_map).clone();
9393
{
94+
let api_inner_map = (&self.inner_map).clone();
9495
actix_rt::System::new().block_on(async move {
9596
if enable_api_server {
9697
info!("api server listening on: {}", self.api_server_addr);
9798
tokio::spawn(async move {
9899
let api_server = ApiServer::new_server();
100+
let api_latency_server = ApiLatencyServer::new_server(api_inner_map);
99101
tonic::transport::Server::builder()
100102
.add_service(api_server)
103+
.add_service(api_latency_server)
101104
.serve(self.api_server_addr.get_sock_addr())
102105
.await?;
103106
Ok::<(), tonic::transport::Error>(())

src/config/to_chainable_builder.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use crate::config::{
2-
BlackHoleConfig, DirectConfig, GrpcConfig, Http2Config, ShadowsocksConfig, TlsConfig,
3-
TrojanConfig, VmessConfig, WebsocketConfig, SS_LOCAL_SHARED_CONTEXT,
2+
BlackHoleConfig, DirectConfig, GrpcConfig, Http2Config, ShadowsocksConfig, SimpleObfsConfig,
3+
TlsConfig, TrojanConfig, VmessConfig, WebsocketConfig, SS_LOCAL_SHARED_CONTEXT,
44
};
55
use crate::proxy::blackhole::BlackHoleStreamBuilder;
66
use crate::proxy::direct::DirectStreamBuilder;
77
use crate::proxy::grpc::GrpcStreamBuilder;
88
use crate::proxy::h2::Http2StreamBuilder;
99
use crate::proxy::shadowsocks::ShadowsocksBuilder;
10+
use crate::proxy::simpleobfs::SimpleObfsStreamBuilder;
1011
use crate::proxy::tls::TlsStreamBuilder;
1112
use crate::proxy::trojan::TrojanStreamBuilder;
1213
use crate::proxy::vmess::vmess_option::VmessOption;
@@ -245,6 +246,27 @@ impl ToChainableStreamBuilder for GrpcConfig {
245246
}
246247
}
247248

249+
impl ToChainableStreamBuilder for SimpleObfsConfig {
250+
fn to_chainable_stream_builder(
251+
&self,
252+
_addr: Option<Address>,
253+
) -> Box<dyn ChainableStreamBuilder> {
254+
Box::new(SimpleObfsStreamBuilder::new(self.host.clone()))
255+
}
256+
257+
fn tag(&self) -> &str {
258+
self.tag.as_str()
259+
}
260+
261+
fn clone_box(&self) -> Box<dyn ToChainableStreamBuilder> {
262+
Box::new(self.clone())
263+
}
264+
265+
fn get_protocol_type(&self) -> ProtocolType {
266+
ProtocolType::SimpleObfs
267+
}
268+
}
269+
248270
impl ToChainableStreamBuilder for Http2Config {
249271
fn to_chainable_stream_builder(
250272
&self,

src/proxy/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ pub mod grpc;
2323
pub mod h2;
2424
pub mod http;
2525
pub mod shadowsocks;
26+
pub mod simpleobfs;
2627
pub mod socks;
2728
pub mod tls;
2829
pub mod trojan;
@@ -71,6 +72,7 @@ pub enum ProtocolType {
7172
Direct,
7273
H2,
7374
Blackhole,
75+
SimpleObfs,
7476
}
7577

7678
impl ProtocolType {

0 commit comments

Comments
 (0)