mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-12 03:09:01 +08:00
refactor (core): Reworked Trojan URI parser to support IPV6 nodes
This commit is contained in:
parent
1bd2f5f643
commit
0072739f01
6
backend/dist/cron-sync-artifacts.min.js
vendored
6
backend/dist/cron-sync-artifacts.min.js
vendored
File diff suppressed because one or more lines are too long
6
backend/dist/sub-store-parser.loon.min.js
vendored
6
backend/dist/sub-store-parser.loon.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "sub-store",
|
"name": "sub-store",
|
||||||
"version": "2.12.6",
|
"version": "2.12.7",
|
||||||
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and ShadowRocket.",
|
||||||
"main": "src/main.js",
|
"main": "src/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -2,6 +2,8 @@ import { getIfNotBlank, isPresent, isNotBlank, getIfPresent } from '@/utils';
|
|||||||
import getSurgeParser from './peggy/surge';
|
import getSurgeParser from './peggy/surge';
|
||||||
import getLoonParser from './peggy/loon';
|
import getLoonParser from './peggy/loon';
|
||||||
import getQXParser from './peggy/qx';
|
import getQXParser from './peggy/qx';
|
||||||
|
import getTrojanURIParser from './peggy/trojan-uri';
|
||||||
|
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
|
|
||||||
// Parse SS URI format (only supports new SIP002, legacy format is depreciated).
|
// Parse SS URI format (only supports new SIP002, legacy format is depreciated).
|
||||||
@ -240,39 +242,9 @@ function URI_Trojan() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const parse = (line) => {
|
const parse = (line) => {
|
||||||
line = line.split('trojan://')[1];
|
const parser = getTrojanURIParser();
|
||||||
const [server, port] = line.split('@')[1].split('?')[0].split(':');
|
const proxy = parser.parse(line);
|
||||||
const name = decodeURIComponent(line.split('#')[1].trim());
|
return proxy;
|
||||||
let paramArr = line.split('?');
|
|
||||||
let scert = null;
|
|
||||||
const params = new Map();
|
|
||||||
if (paramArr.length > 1) {
|
|
||||||
paramArr = paramArr[1].split('#')[0].split('&');
|
|
||||||
for (const pair of paramArr) {
|
|
||||||
let [key, val] = pair.split('=');
|
|
||||||
// skip empty values
|
|
||||||
val = val.trim();
|
|
||||||
if (val.length > 0) {
|
|
||||||
params.set(key, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
params.get('allowInsecure') === '1' ||
|
|
||||||
params.get('allowInsecure') === 'true'
|
|
||||||
) {
|
|
||||||
scert = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: name || `[Trojan] ${server}`, // trojan uri may have no server tag!
|
|
||||||
type: 'trojan',
|
|
||||||
server,
|
|
||||||
port,
|
|
||||||
password: line.split('@')[0],
|
|
||||||
sni: getIfPresent(params.get('sni')),
|
|
||||||
'skip-cert-verify': getIfPresent(scert),
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
return { name, test, parse };
|
return { name, test, parse };
|
||||||
}
|
}
|
||||||
|
@ -142,7 +142,7 @@ uuid = comma "password" equals uuid:[^=,]+ { proxy.uuid = uuid.join("").trim();
|
|||||||
method = comma "method" equals cipher:cipher {
|
method = comma "method" equals cipher:cipher {
|
||||||
proxy.cipher = cipher;
|
proxy.cipher = cipher;
|
||||||
};
|
};
|
||||||
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none");
|
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"xchacha20-ietf-poly1305"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none");
|
||||||
aead = comma "aead" equals flag:bool { proxy.aead = flag; }
|
aead = comma "aead" equals flag:bool { proxy.aead = flag; }
|
||||||
|
|
||||||
udp_relay = comma "udp-relay" equals flag:bool { proxy.udp = flag; }
|
udp_relay = comma "udp-relay" equals flag:bool { proxy.udp = flag; }
|
||||||
|
@ -140,7 +140,7 @@ uuid = comma "password" equals uuid:[^=,]+ { proxy.uuid = uuid.join("").trim();
|
|||||||
method = comma "method" equals cipher:cipher {
|
method = comma "method" equals cipher:cipher {
|
||||||
proxy.cipher = cipher;
|
proxy.cipher = cipher;
|
||||||
};
|
};
|
||||||
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none");
|
cipher = ("aes-128-gcm"/"aes-192-gcm"/"aes-256-gcm"/"aes-128-cfb"/"aes-192-cfb"/"aes-256-cfb"/"aes-128-ctr"/"aes-192-ctr"/"aes-256-ctr"/"rc4-md5"/"xchacha20-ietf-poly1305"/"chacha20-ietf-poly1305"/"chacha20-ietf"/"chacha20-poly1305"/"chacha20"/"none");
|
||||||
aead = comma "aead" equals flag:bool { proxy.aead = flag; }
|
aead = comma "aead" equals flag:bool { proxy.aead = flag; }
|
||||||
|
|
||||||
udp_relay = comma "udp-relay" equals flag:bool { proxy.udp = flag; }
|
udp_relay = comma "udp-relay" equals flag:bool { proxy.udp = flag; }
|
||||||
|
115
backend/src/core/proxy-utils/parsers/peggy/trojan-uri.js
Normal file
115
backend/src/core/proxy-utils/parsers/peggy/trojan-uri.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import * as peggy from 'peggy';
|
||||||
|
const grammars = String.raw`
|
||||||
|
// global initializer
|
||||||
|
{{
|
||||||
|
function $set(obj, path, value) {
|
||||||
|
if (Object(obj) !== obj) return obj;
|
||||||
|
if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [];
|
||||||
|
path
|
||||||
|
.slice(0, -1)
|
||||||
|
.reduce((a, c, i) => (Object(a[c]) === a[c] ? a[c] : (a[c] = Math.abs(path[i + 1]) >> 0 === +path[i + 1] ? [] : {})), obj)[
|
||||||
|
path[path.length - 1]
|
||||||
|
] = value;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBool(str) {
|
||||||
|
if (typeof str === 'undefined' || str === null) return undefined;
|
||||||
|
return /(TRUE)|1/i.test(str);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
{
|
||||||
|
const proxy = {};
|
||||||
|
const obfs = {};
|
||||||
|
const $ = {};
|
||||||
|
const params = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
start = (trojan) {
|
||||||
|
return proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
trojan = "trojan://" password:password "@" server:server ":" port:port params? name:name?{
|
||||||
|
proxy.type = "trojan";
|
||||||
|
proxy.password = password;
|
||||||
|
proxy.server = server;
|
||||||
|
proxy.port = port;
|
||||||
|
proxy.name = name;
|
||||||
|
|
||||||
|
// name may be empty
|
||||||
|
if (!proxy.name) {
|
||||||
|
proxy.name = server + ":" + port;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
password = match:$[^@]+ {
|
||||||
|
return decodeURIComponent(match);
|
||||||
|
};
|
||||||
|
|
||||||
|
server = ip/domain;
|
||||||
|
|
||||||
|
domain = match:[0-9a-zA-z-_.]+ {
|
||||||
|
const domain = match.join("");
|
||||||
|
if (/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/.test(domain)) {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = & {
|
||||||
|
const start = peg$currPos;
|
||||||
|
let end;
|
||||||
|
let j = start;
|
||||||
|
while (j < input.length) {
|
||||||
|
if (input[j] === ",") break;
|
||||||
|
if (input[j] === ":") end = j;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
peg$currPos = end || j;
|
||||||
|
$.ip = input.substring(start, end).trim();
|
||||||
|
return true;
|
||||||
|
} { return $.ip; }
|
||||||
|
|
||||||
|
port = digits:[0-9]+ {
|
||||||
|
const port = parseInt(digits.join(""), 10);
|
||||||
|
if (port >= 80 && port <= 65535) {
|
||||||
|
return port;
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid port: " + port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params = "?" head:param tail:("&"@param)* {
|
||||||
|
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
||||||
|
proxy.sni = params["sni"] || params["peer"];
|
||||||
|
|
||||||
|
if (toBool(params["ws"])) {
|
||||||
|
proxy.network = "ws";
|
||||||
|
$set(proxy, "ws-opts.path", params["wspath"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy.udp = toBool(params["udp"]);
|
||||||
|
proxy.tfo = toBool(params["tfo"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
param = kv/single;
|
||||||
|
|
||||||
|
kv = key:$[a-z]i+ "=" value:$[^&#]i+ {
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
single = key:$[a-z]i+ {
|
||||||
|
params[key] = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
name = "#" + match:$.* {
|
||||||
|
return decodeURIComponent(match);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
let parser;
|
||||||
|
export default function getParser() {
|
||||||
|
if (!parser) {
|
||||||
|
parser = peggy.generate(grammars);
|
||||||
|
}
|
||||||
|
return parser;
|
||||||
|
}
|
105
backend/src/core/proxy-utils/parsers/peggy/trojan-uri.peg
Normal file
105
backend/src/core/proxy-utils/parsers/peggy/trojan-uri.peg
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// global initializer
|
||||||
|
{{
|
||||||
|
function $set(obj, path, value) {
|
||||||
|
if (Object(obj) !== obj) return obj;
|
||||||
|
if (!Array.isArray(path)) path = path.toString().match(/[^.[\]]+/g) || [];
|
||||||
|
path
|
||||||
|
.slice(0, -1)
|
||||||
|
.reduce((a, c, i) => (Object(a[c]) === a[c] ? a[c] : (a[c] = Math.abs(path[i + 1]) >> 0 === +path[i + 1] ? [] : {})), obj)[
|
||||||
|
path[path.length - 1]
|
||||||
|
] = value;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toBool(str) {
|
||||||
|
if (typeof str === 'undefined' || str === null) return undefined;
|
||||||
|
return /(TRUE)|1/i.test(str);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
{
|
||||||
|
const proxy = {};
|
||||||
|
const obfs = {};
|
||||||
|
const $ = {};
|
||||||
|
const params = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
start = (trojan) {
|
||||||
|
return proxy
|
||||||
|
}
|
||||||
|
|
||||||
|
trojan = "trojan://" password:password "@" server:server ":" port:port params? name:name?{
|
||||||
|
proxy.type = "trojan";
|
||||||
|
proxy.password = password;
|
||||||
|
proxy.server = server;
|
||||||
|
proxy.port = port;
|
||||||
|
proxy.name = name;
|
||||||
|
|
||||||
|
// name may be empty
|
||||||
|
if (!proxy.name) {
|
||||||
|
proxy.name = server + ":" + port;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
password = match:$[^@]+ {
|
||||||
|
return decodeURIComponent(match);
|
||||||
|
};
|
||||||
|
|
||||||
|
server = ip/domain;
|
||||||
|
|
||||||
|
domain = match:[0-9a-zA-z-_.]+ {
|
||||||
|
const domain = match.join("");
|
||||||
|
if (/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/.test(domain)) {
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = & {
|
||||||
|
const start = peg$currPos;
|
||||||
|
let end;
|
||||||
|
let j = start;
|
||||||
|
while (j < input.length) {
|
||||||
|
if (input[j] === ",") break;
|
||||||
|
if (input[j] === ":") end = j;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
peg$currPos = end || j;
|
||||||
|
$.ip = input.substring(start, end).trim();
|
||||||
|
return true;
|
||||||
|
} { return $.ip; }
|
||||||
|
|
||||||
|
port = digits:[0-9]+ {
|
||||||
|
const port = parseInt(digits.join(""), 10);
|
||||||
|
if (port >= 80 && port <= 65535) {
|
||||||
|
return port;
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid port: " + port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params = "?" head:param tail:("&"@param)* {
|
||||||
|
proxy["skip-cert-verify"] = toBool(params["allowInsecure"]);
|
||||||
|
proxy.sni = params["sni"] || params["peer"];
|
||||||
|
|
||||||
|
if (toBool(params["ws"])) {
|
||||||
|
proxy.network = "ws";
|
||||||
|
$set(proxy, "ws-opts.path", params["wspath"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy.udp = toBool(params["udp"]);
|
||||||
|
proxy.tfo = toBool(params["tfo"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
param = kv/single;
|
||||||
|
|
||||||
|
kv = key:$[a-z]i+ "=" value:$[^&#]i+ {
|
||||||
|
params[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
single = key:$[a-z]i+ {
|
||||||
|
params[key] = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
name = "#" + match:$.* {
|
||||||
|
return decodeURIComponent(match);
|
||||||
|
}
|
6
backend/sub-store.min.js
vendored
6
backend/sub-store.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user