Skip to content

Commit

Permalink
feat: add support for vless protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
geekdada committed Mar 14, 2024
1 parent dde0147 commit 960c851
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 12 deletions.
56 changes: 52 additions & 4 deletions src/provider/ClashProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import {
SubscriptionUserinfo,
TrojanNodeConfig,
TuicNodeConfig,
VlessNodeConfig,
VmessNodeConfig,
Socks5NodeConfig,
} from '../types'
import {
lowercaseHeaderKeys,
Expand All @@ -35,13 +37,15 @@ import { GetNodeListFunction, GetSubscriptionUserInfoFunction } from './types'
type SupportConfigTypes =
| ShadowsocksNodeConfig
| VmessNodeConfig
| VlessNodeConfig
| HttpsNodeConfig
| HttpNodeConfig
| ShadowsocksrNodeConfig
| SnellNodeConfig
| TrojanNodeConfig
| TuicNodeConfig
| Hysteria2NodeConfig
| Socks5NodeConfig

const logger = createLogger({
service: 'surgio:ClashProvider',
Expand Down Expand Up @@ -259,6 +263,7 @@ export const parseClashConfig = (
} as ShadowsocksNodeConfig
}

case 'vless':
case 'vmess': {
// istanbul ignore next
if (
Expand All @@ -274,20 +279,36 @@ export const parseClashConfig = (
return undefined
}

const vmessNode: VmessNodeConfig = {
type: NodeTypeEnum.Vmess,
type NodeConfig = VlessNodeConfig | VmessNodeConfig
const nodeType =
item.type === 'vless' ? NodeTypeEnum.Vless : NodeTypeEnum.Vmess
const fallbackCipherMethod = item.type === 'vless' ? 'none' : 'auto'

const vmessNode = {
type: nodeType,
nodeName: item.name,
hostname: item.server,
port: item.port,
uuid: item.uuid,
alterId: item.alterId ? `${item.alterId}` : '0',
method: item.cipher || 'auto',
method: item.cipher || fallbackCipherMethod,
udpRelay: resolveUdpRelay(item.udp, udpRelay),
tls: item.tls === true,
network: item.network || 'tcp',
} as NodeConfig

if (vmessNode.type === NodeTypeEnum.Vless && item.tls !== true) {
logger.warn(
`未经 TLS 传输的 Vless 协议不安全并且不被 Surgio 支持,节点 ${item.name} 会被省略`,
)
return undefined
}

if (vmessNode.tls) {
if (vmessNode.type === NodeTypeEnum.Vless) {
vmessNode.flow = item.flow
}

if (vmessNode.type === NodeTypeEnum.Vmess && vmessNode.tls) {
if (typeof item.servername === 'string') {
vmessNode.sni = item.servername
}
Expand Down Expand Up @@ -477,6 +498,33 @@ export const parseClashConfig = (
: null),
} as Hysteria2NodeConfig

case 'socks5': {
const socks5Node = {
type: NodeTypeEnum.Socks5,
nodeName: item.name,
hostname: item.server,
port: item.port,
} as Socks5NodeConfig

if (item.username) {
socks5Node.username = item.username
}
if (item.password) {
socks5Node.password = item.password
}
if (item.udp) {
socks5Node.udpRelay = item.udp
}
if (item.tls) {
socks5Node.tls = item.tls
}
if (item['skip-cert-verify']) {
socks5Node.skipCertVerify = item['skip-cert-verify']
}

return socks5Node
}

default:
logger.warn(
`不支持从 Clash 订阅中读取 ${item.type} 的节点,节点 ${item.name} 会被省略`,
Expand Down
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
Hysteria2NodeConfigValidator,
ClashCoreValidator,
} from './validators'
import { VlessNodeConfigValidator } from './validators/vless'

export enum NodeTypeEnum {
HTTPS = 'https',
Expand All @@ -29,6 +30,7 @@ export enum NodeTypeEnum {
Shadowsocksr = 'shadowsocksr',
Snell = 'snell',
Vmess = 'vmess',
Vless = 'vless',
Trojan = 'trojan',
Socks5 = 'socks5',
Tuic = 'tuic',
Expand Down Expand Up @@ -162,6 +164,9 @@ export type SnellNodeConfig = z.infer<typeof SnellNodeConfigValidator> &
export type VmessNodeConfig = z.infer<typeof VmessNodeConfigValidator> &
SurgioInternals

export type VlessNodeConfig = z.infer<typeof VlessNodeConfigValidator> &
SurgioInternals

export type TuicNodeConfig = z.infer<typeof TuicNodeConfigValidator> &
SurgioInternals

Expand Down Expand Up @@ -205,6 +210,7 @@ export type PossibleNodeConfigType =
| ShadowsocksrNodeConfig
| SnellNodeConfig
| VmessNodeConfig
| VlessNodeConfig
| TrojanNodeConfig
| Socks5NodeConfig
| TuicNodeConfig
Expand Down
17 changes: 14 additions & 3 deletions src/utils/clash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,27 @@ function nodeListMapper(nodeConfig: PossibleNodeConfigType) {
: null),
} as const

case NodeTypeEnum.Vless:
case NodeTypeEnum.Vmess: {
const vmessNode: Record<string, any> = {
type: 'vmess',
type: nodeConfig.type,
cipher: nodeConfig.method,
name: nodeConfig.nodeName,
server: nodeConfig.hostname,
port: nodeConfig.port,
udp: nodeConfig.udpRelay === true,
uuid: nodeConfig.uuid,
alterId: nodeConfig.alterId || '0',
network: nodeConfig.network || 'tcp',
}

if (nodeConfig.type === NodeTypeEnum.Vmess) {
vmessNode.alertId = nodeConfig.alterId || '0'
}

if (nodeConfig.type === NodeTypeEnum.Vless) {
vmessNode.flow = nodeConfig.flow
}

switch (nodeConfig.network) {
case 'tcp':
break
Expand Down Expand Up @@ -177,7 +185,10 @@ function nodeListMapper(nodeConfig: PossibleNodeConfigType) {
break
}

if (nodeConfig.tls) {
if (
(nodeConfig.type === NodeTypeEnum.Vmess && nodeConfig.tls) ||
nodeConfig.type === NodeTypeEnum.Vless
) {
vmessNode.tls = true

if (nodeConfig.skipCertVerify) {
Expand Down
5 changes: 3 additions & 2 deletions src/utils/loon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export const getLoonNodes = function (
return config.join(',')
}

case NodeTypeEnum.Vless:
case NodeTypeEnum.Vmess: {
if (
!LOON_SUPPORTED_VMESS_NETWORK.includes(nodeConfig.network as any)
Expand All @@ -104,7 +105,7 @@ export const getLoonNodes = function (
}

const config: Array<string | number> = [
`${nodeConfig.nodeName} = vmess`,
`${nodeConfig.nodeName} = ${nodeConfig.type}`,
nodeConfig.hostname,
nodeConfig.port,
nodeConfig.method === 'auto'
Expand Down Expand Up @@ -141,7 +142,7 @@ export const getLoonNodes = function (
}
}

if (nodeConfig.tls) {
if (nodeConfig.type === NodeTypeEnum.Vmess && nodeConfig.tls) {
config.push(`over-tls=true`)

if (nodeConfig.sni) {
Expand Down
13 changes: 10 additions & 3 deletions src/utils/quantumult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ function nodeListMapper(
nodeConfig: PossibleNodeConfigType,
): [string, string] | undefined {
switch (nodeConfig.type) {
case NodeTypeEnum.Vless:
case NodeTypeEnum.Vmess: {
if (
!QUANTUMULT_X_SUPPORTED_VMESS_NETWORK.includes(
Expand All @@ -69,7 +70,10 @@ function nodeListMapper(

switch (nodeConfig.network) {
case 'ws':
if (nodeConfig.tls) {
if (
(nodeConfig.type === NodeTypeEnum.Vmess && nodeConfig.tls) ||
nodeConfig.type === NodeTypeEnum.Vless
) {
config.push(`obfs=wss`)

// istanbul ignore next
Expand Down Expand Up @@ -97,7 +101,10 @@ function nodeListMapper(

break
case 'tcp':
if (nodeConfig.tls) {
if (
(nodeConfig.type === NodeTypeEnum.Vmess && nodeConfig.tls) ||
nodeConfig.type === NodeTypeEnum.Vless
) {
config.push(`obfs=over-tls`)

if (nodeConfig.skipCertVerify) {
Expand Down Expand Up @@ -144,7 +151,7 @@ function nodeListMapper(
)
}

return [nodeConfig.nodeName, `vmess=${config.join(', ')}`]
return [nodeConfig.nodeName, `${nodeConfig.type}=${config.join(', ')}`]
}

case NodeTypeEnum.Shadowsocks: {
Expand Down
27 changes: 27 additions & 0 deletions src/validators/vless.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { z } from 'zod'

import { NodeTypeEnum } from '../types'
import { PortValidator, TlsNodeConfigValidator } from './common'
import {
VmessNetworkValidator,
VmessH2OptsValidator,
VmessGRPCOptsValidator,
VmessHttpOptsValidator,
VmessWSOptsValidator,
} from './vmess'

export const VlessNodeConfigValidator = TlsNodeConfigValidator.extend({
type: z.literal(NodeTypeEnum.Vless),
hostname: z.string(),
port: PortValidator,
method: z.literal('none').optional().default('none'),
uuid: z.string().uuid(),
network: VmessNetworkValidator,
udpRelay: z.oboolean(),
flow: z.string(),

wsOpts: VmessWSOptsValidator.optional(),
h2Opts: VmessH2OptsValidator.optional(),
httpOpts: VmessHttpOptsValidator.optional(),
grpcOpts: VmessGRPCOptsValidator.optional(),
})

0 comments on commit 960c851

Please sign in to comment.