Skip to content

Commit 3bc153a

Browse files
committed
feat: Shadowsocks 支持更多传输层
1 parent 2944399 commit 3bc153a

3 files changed

Lines changed: 197 additions & 11 deletions

File tree

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sub-store",
3-
"version": "2.20.73",
3+
"version": "2.20.74",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
55
"main": "src/main.js",
66
"scripts": {

backend/src/core/proxy-utils/parsers/index.js

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import getTrojanURIParser from './peggy/trojan-uri';
1414
import $ from '@/core/app';
1515
import JSON5 from 'json5';
1616
import YAML from '@/utils/yaml';
17+
import _ from 'lodash';
1718

1819
import { Base64 } from 'js-base64';
1920

@@ -121,7 +122,6 @@ function URI_SOCKS() {
121122
// Parse SS URI format (only supports new SIP002, legacy format is depreciated).
122123
// reference: https://github.com/shadowsocks/shadowsocks-org/wiki/SIP002-URI-Scheme
123124
function URI_SS() {
124-
// TODO: 暂不支持 httpupgrade
125125
const name = 'URI SS Parser';
126126
const test = (line) => {
127127
return /^ss:\/\//.test(line);
@@ -174,6 +174,93 @@ function URI_SS() {
174174
const parsed = content.match(/(\?.*)$/);
175175
query = parsed[1];
176176
}
177+
const params = {};
178+
for (const addon of query.replace(/^\?/, '').split('&')) {
179+
if (addon) {
180+
const [key, valueRaw] = addon.split('=');
181+
let value = valueRaw;
182+
value = decodeURIComponent(valueRaw);
183+
params[key] = value;
184+
}
185+
}
186+
proxy.tls = params.security && params.security !== 'none';
187+
proxy['skip-cert-verify'] = !!params['allowInsecure'];
188+
proxy.sni = params['sni'] || params['peer'];
189+
proxy['client-fingerprint'] = params.fp;
190+
proxy.alpn = params.alpn
191+
? decodeURIComponent(params.alpn).split(',')
192+
: undefined;
193+
194+
if (params['ws']) {
195+
proxy.network = 'ws';
196+
_.set(proxy, 'ws-opts.path', params['wspath']);
197+
}
198+
199+
if (params['type']) {
200+
let httpupgrade;
201+
proxy.network = params['type'];
202+
if (proxy.network === 'httpupgrade') {
203+
proxy.network = 'ws';
204+
httpupgrade = true;
205+
}
206+
if (['grpc'].includes(proxy.network)) {
207+
proxy[proxy.network + '-opts'] = {
208+
'grpc-service-name': params['serviceName'],
209+
'_grpc-type': params['mode'],
210+
'_grpc-authority': params['authority'],
211+
};
212+
} else {
213+
if (params['path']) {
214+
_.set(
215+
proxy,
216+
proxy.network + '-opts.path',
217+
decodeURIComponent(params['path']),
218+
);
219+
}
220+
if (params['host']) {
221+
_.set(
222+
proxy,
223+
proxy.network + '-opts.headers.Host',
224+
decodeURIComponent(params['host']),
225+
);
226+
}
227+
if (httpupgrade) {
228+
_.set(
229+
proxy,
230+
proxy.network + '-opts.v2ray-http-upgrade',
231+
true,
232+
);
233+
_.set(
234+
proxy,
235+
proxy.network + '-opts.v2ray-http-upgrade-fast-open',
236+
true,
237+
);
238+
}
239+
}
240+
if (['reality'].includes(params.security)) {
241+
const opts = {};
242+
if (params.pbk) {
243+
opts['public-key'] = params.pbk;
244+
}
245+
if (params.sid) {
246+
opts['short-id'] = params.sid;
247+
}
248+
if (params.spx) {
249+
opts['_spider-x'] = params.spx;
250+
}
251+
if (params.mode) {
252+
proxy._mode = params.mode;
253+
}
254+
if (params.extra) {
255+
proxy._extra = params.extra;
256+
}
257+
if (Object.keys(opts).length > 0) {
258+
_.set(proxy, params.security + '-opts', opts);
259+
}
260+
}
261+
}
262+
263+
proxy.udp = !!params['udp'];
177264

178265
const serverAndPort = serverAndPortArray[1];
179266
const portIdx = serverAndPort.lastIndexOf(':');

backend/src/core/proxy-utils/producers/uri.js

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -174,26 +174,27 @@ export default function URI_Producer() {
174174
)}:${encodeURIComponent(proxy.password)}`
175175
: Base64.encode(userinfo)
176176
}@${proxy.server}:${proxy.port}${proxy.plugin ? '/' : ''}`;
177+
let query = '';
177178
if (proxy.plugin) {
178-
result += '?plugin=';
179+
query += '&plugin=';
179180
const opts = proxy['plugin-opts'];
180181
switch (proxy.plugin) {
181182
case 'obfs':
182-
result += encodeURIComponent(
183+
query += encodeURIComponent(
183184
`simple-obfs;obfs=${opts.mode}${
184185
opts.host ? ';obfs-host=' + opts.host : ''
185186
}`,
186187
);
187188
break;
188189
case 'v2ray-plugin':
189-
result += encodeURIComponent(
190+
query += encodeURIComponent(
190191
`v2ray-plugin;obfs=${opts.mode}${
191192
opts.host ? ';obfs-host' + opts.host : ''
192193
}${opts.tls ? ';tls' : ''}`,
193194
);
194195
break;
195196
case 'shadow-tls':
196-
result += encodeURIComponent(
197+
query += encodeURIComponent(
197198
`shadow-tls;host=${opts.host};password=${opts.password};version=${opts.version}`,
198199
);
199200
break;
@@ -204,14 +205,112 @@ export default function URI_Producer() {
204205
}
205206
}
206207
if (proxy['udp-over-tcp']) {
207-
result = `${result}${proxy.plugin ? '&' : '?'}uot=1`;
208+
query += '&uot=1';
208209
}
209210
if (proxy.tfo) {
210-
result = `${result}${
211-
proxy.plugin || proxy['udp-over-tcp'] ? '&' : '?'
212-
}tfo=1`;
211+
query += '&tfo=1';
213212
}
214-
result += `#${encodeURIComponent(proxy.name)}`;
213+
let ssTransport = '';
214+
if (proxy.network) {
215+
let ssType = proxy.network;
216+
if (
217+
proxy.network === 'ws' &&
218+
proxy['ws-opts']?.['v2ray-http-upgrade']
219+
) {
220+
ssType = 'httpupgrade';
221+
}
222+
ssTransport = `&type=${encodeURIComponent(ssType)}`;
223+
if (['grpc'].includes(proxy.network)) {
224+
let ssTransportServiceName =
225+
proxy[`${proxy.network}-opts`]?.[
226+
`${proxy.network}-service-name`
227+
];
228+
let ssTransportAuthority =
229+
proxy[`${proxy.network}-opts`]?.['_grpc-authority'];
230+
if (ssTransportServiceName) {
231+
ssTransport += `&serviceName=${encodeURIComponent(
232+
ssTransportServiceName,
233+
)}`;
234+
}
235+
if (ssTransportAuthority) {
236+
ssTransport += `&authority=${encodeURIComponent(
237+
ssTransportAuthority,
238+
)}`;
239+
}
240+
ssTransport += `&mode=${encodeURIComponent(
241+
proxy[`${proxy.network}-opts`]?.['_grpc-type'] ||
242+
'gun',
243+
)}`;
244+
}
245+
let ssTransportPath = proxy[`${proxy.network}-opts`]?.path;
246+
let ssTransportHost =
247+
proxy[`${proxy.network}-opts`]?.headers?.Host;
248+
if (ssTransportPath) {
249+
ssTransport += `&path=${encodeURIComponent(
250+
Array.isArray(ssTransportPath)
251+
? ssTransportPath[0]
252+
: ssTransportPath,
253+
)}`;
254+
}
255+
if (ssTransportHost) {
256+
ssTransport += `&host=${encodeURIComponent(
257+
Array.isArray(ssTransportHost)
258+
? ssTransportHost[0]
259+
: ssTransportHost,
260+
)}`;
261+
}
262+
}
263+
let ssFp = '';
264+
if (proxy['client-fingerprint']) {
265+
ssFp = `&fp=${encodeURIComponent(
266+
proxy['client-fingerprint'],
267+
)}`;
268+
}
269+
let ssAlpn = '';
270+
if (proxy.alpn) {
271+
ssAlpn = `&alpn=${encodeURIComponent(
272+
Array.isArray(proxy.alpn)
273+
? proxy.alpn
274+
: proxy.alpn.join(','),
275+
)}`;
276+
}
277+
const ssIsReality = proxy['reality-opts'];
278+
let ssSid = '';
279+
let ssPbk = '';
280+
let ssSpx = '';
281+
let ssSecurity = proxy.tls ? '&security=tls' : '';
282+
let ssMode = '';
283+
let ssExtra = '';
284+
if (ssIsReality) {
285+
ssSecurity = `&security=reality`;
286+
const publicKey = proxy['reality-opts']?.['public-key'];
287+
if (publicKey) {
288+
ssPbk = `&pbk=${encodeURIComponent(publicKey)}`;
289+
}
290+
const shortId = proxy['reality-opts']?.['short-id'];
291+
if (shortId) {
292+
ssSid = `&sid=${encodeURIComponent(shortId)}`;
293+
}
294+
const spiderX = proxy['reality-opts']?.['_spider-x'];
295+
if (spiderX) {
296+
ssSpx = `&spx=${encodeURIComponent(spiderX)}`;
297+
}
298+
if (proxy._extra) {
299+
ssExtra = `&extra=${encodeURIComponent(proxy._extra)}`;
300+
}
301+
if (proxy._mode) {
302+
ssMode = `&mode=${encodeURIComponent(proxy._mode)}`;
303+
}
304+
}
305+
if (proxy.tls) {
306+
query += `&sni=${encodeURIComponent(
307+
proxy.sni || proxy.server,
308+
)}${proxy['skip-cert-verify'] ? '&allowInsecure=1' : ''}`;
309+
}
310+
query += `${ssTransport}${ssAlpn}${ssFp}${ssSecurity}${ssSid}${ssPbk}${ssSpx}${ssMode}${ssExtra}#${encodeURIComponent(
311+
proxy.name,
312+
)}`;
313+
result += query.replace(/^&/, '?');
215314
break;
216315
case 'ssr':
217316
result = `${proxy.server}:${proxy.port}:${proxy.protocol}:${

0 commit comments

Comments
 (0)