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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use crate::models::{
Proxy, SOCKS_DEFAULT_GROUP, SSR_DEFAULT_GROUP, SS_CIPHERS, SS_DEFAULT_GROUP,
};
use serde_json::{from_str, Value};
/// Parse a SSTap JSON configuration into a vector of Proxy objects
/// Based on the C++ implementation in explodeSSTap function
pub fn explode_sstap(content: &str, nodes: &mut Vec<Proxy>) -> bool {
// Parse the JSON content
let json: Value = match from_str(content) {
Ok(json) => json,
Err(_) => return false,
};
// Check if it has configs array
if !json["configs"].is_array() {
return false;
}
let configs = json["configs"].as_array().unwrap();
if configs.is_empty() {
return false;
}
let mut index = nodes.len();
let mut success = false;
for config in configs {
// Extract common fields
let group = config["group"].as_str().unwrap_or("");
let remarks = config["remarks"].as_str().unwrap_or("");
let server = config["server"].as_str().unwrap_or("");
let port = config["server_port"].as_u64().unwrap_or(0) as u16;
// Skip if port is 0
if port == 0 {
continue;
}
// Use server:port as remark if not provided
let final_remarks = if remarks.is_empty() {
format!("{} ({})", server, port)
} else {
remarks.to_string()
};
// Extract password
let password = config["password"].as_str().unwrap_or("");
// Get config type
let config_type = config["type"].as_u64().unwrap_or(0);
match config_type {
5 => {
// Socks 5
let username = config["username"].as_str().unwrap_or("");
// Create Socks5 proxy
let mut node = Proxy::socks_construct(
if group.is_empty() {
SOCKS_DEFAULT_GROUP
} else {
group
},
&final_remarks,
server,
port,
username,
password,
None,
None,
None,
"",
);
node.id = index as u32;
nodes.push(node);
index += 1;
success = true;
}
6 => {
// SS/SSR
let protocol = config["protocol"].as_str().unwrap_or("");
let obfs = config["obfs"].as_str().unwrap_or("");
let method = config["method"].as_str().unwrap_or("");
// Check if it's SS or SSR
if SS_CIPHERS.iter().any(|c| *c == method)
&& protocol == "origin"
&& obfs == "plain"
{
// Is Shadowsocks
let mut node = Proxy::ss_construct(
if group.is_empty() {
SS_DEFAULT_GROUP
} else {
group
},
&final_remarks,
server,
port,
password,
method,
"",
"",
None,
None,
None,
None,
"",
);
node.id = index as u32;
nodes.push(node);
index += 1;
success = true;
} else {
// Is ShadowsocksR
let obfs_param = config["obfsparam"].as_str().unwrap_or("");
let proto_param = config["protocolparam"].as_str().unwrap_or("");
let mut node = Proxy::ssr_construct(
if group.is_empty() {
SSR_DEFAULT_GROUP
} else {
group
},
&final_remarks,
server,
port,
protocol,
method,
obfs,
password,
obfs_param,
proto_param,
None,
None,
None,
"",
);
node.id = index as u32;
nodes.push(node);
index += 1;
success = true;
}
}
_ => continue, // Skip unknown type
}
}
success
}