Skip to content

Commit

Permalink
feat: Support port-hopping config from Clash subscription
Browse files Browse the repository at this point in the history
  • Loading branch information
geekdada committed Sep 17, 2024
1 parent ecc6901 commit 1869a1f
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 18 deletions.
91 changes: 73 additions & 18 deletions src/provider/ClashProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ClashProviderConfig,
HttpNodeConfig,
HttpsNodeConfig,
Hysteria2NodeConfigInput,
Hysteria2NodeConfig,
NodeTypeEnum,
ShadowsocksNodeConfig,
Expand All @@ -23,6 +24,7 @@ import {
VlessNodeConfig,
VmessNodeConfig,
Socks5NodeConfig,
TuicNodeConfigInput,
} from '../types'
import {
lowercaseHeaderKeys,
Expand All @@ -31,6 +33,10 @@ import {
parseBitrate,
} from '../utils'
import relayableUrl from '../utils/relayable-url'
import {
Hysteria2NodeConfigValidator,
TuicNodeConfigValidator,
} from '../validators'

import Provider from './Provider'
import { GetNodeListFunction, GetSubscriptionUserInfoFunction } from './types'
Expand Down Expand Up @@ -461,8 +467,10 @@ export const parseClashConfig = (
}

case 'tuic': {
let input: TuicNodeConfigInput

if (item.version >= 5) {
return {
input = {
type: NodeTypeEnum.Tuic,
version: item.version,
nodeName: item.name,
Expand All @@ -476,33 +484,60 @@ export const parseClashConfig = (
tls13: tls13 ?? false,
...('sni' in item ? { sni: item.sni } : null),
...('alpn' in item ? { alpn: item.alpn } : null),
} as TuicNodeConfig
...('ports' in item
? {
portHopping: item.ports,
}
: null),
...('hop-interval' in item
? { portHoppingInterval: item['hop-interval'] }
: null),
}
} else {
input = {
type: NodeTypeEnum.Tuic,
nodeName: item.name,
hostname: item.server,
port: item.port,
token: item.token,
...('skip-cert-verify' in item
? { skipCertVerify: item['skip-cert-verify'] === true }
: null),
tls13: tls13 ?? false,
...('sni' in item ? { sni: item.sni } : null),
...('alpn' in item ? { alpn: item.alpn } : null),
...('ports' in item
? {
portHopping: item.ports,
}
: null),
...('hop-interval' in item
? { portHoppingInterval: item['hop-interval'] }
: null),
}
}

return {
type: NodeTypeEnum.Tuic,
nodeName: item.name,
hostname: item.server,
port: item.port,
token: item.token,
...('skip-cert-verify' in item
? { skipCertVerify: item['skip-cert-verify'] === true }
: null),
tls13: tls13 ?? false,
...('sni' in item ? { sni: item.sni } : null),
...('alpn' in item ? { alpn: item.alpn } : null),
} as TuicNodeConfig
const result = TuicNodeConfigValidator.safeParse(input)

// istanbul ignore next
if (!result.success) {
throw new SurgioError('Tuic 节点配置校验失败', {
cause: result.error,
})
}

return result.data
}

case 'hysteria2':
case 'hysteria2': {
// istanbul ignore next
if (item.obfs && item.obfs !== 'salamander') {
throw new Error(
'不支持从 Clash 订阅中读取 Hysteria2 节点,因为其 obfs 不是 salamander',
)
}

return {
const input: Hysteria2NodeConfigInput = {
type: NodeTypeEnum.Hysteria2,
nodeName: item.name,
hostname: item.server,
Expand All @@ -521,7 +556,27 @@ export const parseClashConfig = (
...('skip-cert-verify' in item
? { skipCertVerify: item['skip-cert-verify'] === true }
: null),
} as Hysteria2NodeConfig
...('ports' in item
? {
portHopping: item.ports,
}
: null),
...('hop-interval' in item
? { portHoppingInterval: item['hop-interval'] }
: null),
}

const result = Hysteria2NodeConfigValidator.safeParse(input)

// istanbul ignore next
if (!result.success) {
throw new SurgioError('Hysteria2 节点配置校验失败', {
cause: result.error,
})
}

return result.data
}

case 'socks5': {
const socks5Node = {
Expand Down
2 changes: 2 additions & 0 deletions src/provider/__tests__/ClashProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,8 @@ test('getClashSubscription', async (t) => {
alpn: ['h3'],
skipCertVerify: false,
sni: 'server.com',
portHopping: '5000-6000;7000',
portHoppingInterval: 10,
})
})

Expand Down
2 changes: 2 additions & 0 deletions src/provider/__tests__/__snapshots__/ClashProvider.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ Generated by [AVA](https://avajs.dev).
obfsPassword: 'yourpassword',
password: 'yourpassword',
port: 443,
portHopping: '5000-6000;7000',
portHoppingInterval: 10,
skipCertVerify: false,
sni: 'server.com',
type: 'hysteria2',
Expand Down
Binary file modified src/provider/__tests__/__snapshots__/ClashProvider.test.ts.snap
Binary file not shown.
2 changes: 2 additions & 0 deletions test/asset/clash-sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ Proxy:
- h3
# ca: "./my.ca"
# ca-str: "xyz"
ports: "5000-6000,7000"
hop-interval: 10

Proxy Group:
# url-test select which proxy will be used by benchmarking speed to a URL.
Expand Down

0 comments on commit 1869a1f

Please sign in to comment.