mirror of
https://github.com/yanue/V2rayU.git
synced 2025-06-27 05:30:09 +00:00
1434 lines
55 KiB
Swift
1434 lines
55 KiB
Swift
//
|
||
// V2rayConfig.swift
|
||
// V2rayU
|
||
//
|
||
// Created by yanue on 2018/10/25.
|
||
// Copyright © 2018 yanue. All rights reserved.
|
||
//
|
||
|
||
import Cocoa
|
||
import SwiftyJSON
|
||
import JavaScriptCore
|
||
|
||
let jsSourceFormatConfig =
|
||
"""
|
||
/**
|
||
* V2ray Config Format
|
||
* @return {string}
|
||
*/
|
||
var V2rayConfigFormat = function (encodeV2rayStr, encodeDnsStr) {
|
||
var deV2rayStr = decodeURIComponent(encodeV2rayStr);
|
||
if (!deV2rayStr) {
|
||
return "error: cannot decode uri"
|
||
}
|
||
|
||
var dns = {};
|
||
try {
|
||
dns = JSON.parse(decodeURIComponent(encodeDnsStr));
|
||
} catch (e) {
|
||
console.log("error", e);
|
||
}
|
||
|
||
try {
|
||
var obj = JSON.parse(deV2rayStr);
|
||
if (!obj) {
|
||
return "error: cannot parse json"
|
||
}
|
||
|
||
var v2rayConfig = {};
|
||
// ordered keys
|
||
v2rayConfig["log"] = obj.log;
|
||
v2rayConfig["inbounds"] = obj.inbounds;
|
||
v2rayConfig["inbound"] = obj.inbound;
|
||
v2rayConfig["inboundDetour"] = obj.inboundDetour;
|
||
v2rayConfig["outbounds"] = obj.outbounds;
|
||
v2rayConfig["outbound"] = obj.outbound;
|
||
v2rayConfig["outboundDetour"] = obj.outboundDetour;
|
||
v2rayConfig["api"] = obj.api;
|
||
v2rayConfig["dns"] = dns;
|
||
v2rayConfig["stats"] = obj.stats;
|
||
v2rayConfig["routing"] = obj.routing;
|
||
v2rayConfig["policy"] = obj.policy;
|
||
v2rayConfig["reverse"] = obj.reverse;
|
||
v2rayConfig["transport"] = obj.transport;
|
||
|
||
return JSON.stringify(v2rayConfig, null, 2);
|
||
} catch (e) {
|
||
console.log("error", e);
|
||
return "error: " + e.toString()
|
||
}
|
||
};
|
||
|
||
|
||
/**
|
||
* json beauty Format
|
||
* @return {string}
|
||
*/
|
||
var JsonBeautyFormat = function (en64Str) {
|
||
var deStr = decodeURIComponent(en64Str);
|
||
if (!deStr) {
|
||
return "error: cannot decode uri"
|
||
}
|
||
try {
|
||
var obj = JSON.parse(deStr);
|
||
if (!obj) {
|
||
return "error: cannot parse json"
|
||
}
|
||
|
||
return JSON.stringify(obj, null, 2);
|
||
} catch (e) {
|
||
console.log("error", e);
|
||
return "error: " + e.toString()
|
||
}
|
||
};
|
||
"""
|
||
|
||
|
||
class V2rayConfig: NSObject {
|
||
// routing rule tag
|
||
enum RoutingRule: Int {
|
||
case RoutingRuleGlobal = 0 // Global
|
||
case RoutingRuleLAN = 1 // Bypassing the LAN Address
|
||
case RoutingRuleCn = 2 // Bypassing mainland address
|
||
case RoutingRuleLANAndCn = 3 // Bypassing LAN and mainland address
|
||
}
|
||
|
||
var v2ray: V2rayStruct = V2rayStruct()
|
||
var isValid = false
|
||
var isNewVersion = true
|
||
var isEmptyInput = false
|
||
|
||
var error = ""
|
||
var errors: [String] = []
|
||
|
||
// base
|
||
var logLevel = "info"
|
||
var socksPort = "1080"
|
||
var socksHost = "127.0.0.1"
|
||
var httpPort = "1087"
|
||
var httpHost = "127.0.0.1"
|
||
var enableUdp = true
|
||
var enableMux = false
|
||
var enableSniffing = false
|
||
var mux = 8
|
||
var dnsJson = UserDefaults.get(forKey: .v2rayDnsJson) ?? ""
|
||
|
||
// server
|
||
var serverProtocol = V2rayProtocolOutbound.vmess.rawValue
|
||
var serverVmess = V2rayOutboundVMessItem()
|
||
var serverSocks5 = V2rayOutboundSocks()
|
||
var serverShadowsocks = V2rayOutboundShadowsockServer()
|
||
|
||
// transfor
|
||
var streamNetwork = V2rayStreamSettings.network.tcp.rawValue
|
||
var streamTcp = TcpSettings()
|
||
var streamKcp = KcpSettings()
|
||
var streamDs = DsSettings()
|
||
var streamWs = WsSettings()
|
||
var streamH2 = HttpSettings()
|
||
var streamQuic = QuicSettings()
|
||
var routing = V2rayRouting()
|
||
|
||
// tls
|
||
var streamTlsSecurity = "none"
|
||
var streamTlsAllowInsecure = true
|
||
var streamTlsServerName = ""
|
||
|
||
var routingDomainStrategy: V2rayRoutingSetting.domainStrategy = .AsIs
|
||
var routingRule: RoutingRule = .RoutingRuleGlobal
|
||
let routingProxyDomains = UserDefaults.getArray(forKey: .routingProxyDomains) ?? [];
|
||
let routingProxyIps = UserDefaults.getArray(forKey: .routingProxyIps) ?? [];
|
||
let routingDirectDomains = UserDefaults.getArray(forKey: .routingDirectDomains) ?? [];
|
||
let routingDirectIps = UserDefaults.getArray(forKey: .routingDirectIps) ?? [];
|
||
let routingBlockDomains = UserDefaults.getArray(forKey: .routingBlockDomains) ?? [];
|
||
let routingBlockIps = UserDefaults.getArray(forKey: .routingBlockIps) ?? [];
|
||
|
||
private var foundHttpPort = false
|
||
private var foundSockPort = false
|
||
private var foundServerProtocol = false
|
||
|
||
// Initialization
|
||
override init() {
|
||
super.init()
|
||
|
||
self.enableMux = UserDefaults.getBool(forKey: .enableMux)
|
||
self.enableUdp = UserDefaults.getBool(forKey: .enableUdp)
|
||
self.enableSniffing = UserDefaults.getBool(forKey: .enableSniffing)
|
||
|
||
self.httpPort = UserDefaults.get(forKey: .localHttpPort) ?? "1087"
|
||
self.httpHost = UserDefaults.get(forKey: .localHttpHost) ?? "127.0.0.1"
|
||
self.socksPort = UserDefaults.get(forKey: .localSockPort) ?? "1080"
|
||
self.socksHost = UserDefaults.get(forKey: .localSockHost) ?? "127.0.0.1"
|
||
|
||
self.mux = Int(UserDefaults.get(forKey: .muxConcurrent) ?? "8") ?? 8
|
||
|
||
self.logLevel = UserDefaults.get(forKey: .v2rayLogLevel) ?? "info"
|
||
|
||
// routing
|
||
self.routingDomainStrategy = V2rayRoutingSetting.domainStrategy(rawValue: UserDefaults.get(forKey: .routingDomainStrategy) ?? "AsIs") ?? .AsIs
|
||
self.routingRule = RoutingRule(rawValue: Int(UserDefaults.get(forKey: .routingRule) ?? "0") ?? 0) ?? .RoutingRuleGlobal
|
||
}
|
||
|
||
// combine manual edited data
|
||
// by manual tab view
|
||
func combineManual() -> String {
|
||
// combine data
|
||
self.combineManualData()
|
||
|
||
// 1. encode to json text
|
||
let encoder = JSONEncoder()
|
||
let data = try! encoder.encode(self.v2ray)
|
||
var jsonStr = String(data: data, encoding: .utf8)!
|
||
|
||
// 2. format json text by javascript
|
||
jsonStr = self.formatJson(json: jsonStr)
|
||
|
||
return jsonStr
|
||
}
|
||
|
||
func formatJson(json: String) -> String {
|
||
var jsonStr = json
|
||
if let context = JSContext() {
|
||
context.evaluateScript(jsSourceFormatConfig)
|
||
// call js func
|
||
if let formatFunction = context.objectForKeyedSubscript("V2rayConfigFormat") {
|
||
let escapedV2String = jsonStr.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
|
||
let escapedDnsString = self.dnsJson.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
|
||
if let result = formatFunction.call(withArguments: [escapedV2String as Any, escapedDnsString as Any]) {
|
||
// error occurred with prefix "error:"
|
||
if let reStr = result.toString(), reStr.count > 0 {
|
||
if !reStr.hasPrefix("error:") {
|
||
// replace json str
|
||
jsonStr = reStr
|
||
} else {
|
||
self.error = reStr
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return jsonStr
|
||
}
|
||
|
||
func combineManualData() {
|
||
// base
|
||
self.v2ray.log.loglevel = V2rayLog.logLevel(rawValue: UserDefaults.get(forKey: .v2rayLogLevel) ?? "info") ?? V2rayLog.logLevel.info
|
||
|
||
// ------------------------------------- inbound start ---------------------------------------------
|
||
var inHttp = V2rayInbound()
|
||
inHttp.port = self.httpPort
|
||
inHttp.listen = self.httpHost
|
||
inHttp.protocol = V2rayProtocolInbound.http
|
||
if self.enableSniffing {
|
||
inHttp.sniffing = V2rayInboundSniffing()
|
||
}
|
||
|
||
var inSocks = V2rayInbound()
|
||
inSocks.port = self.socksPort
|
||
inSocks.listen = self.socksHost
|
||
inSocks.protocol = V2rayProtocolInbound.socks
|
||
inSocks.settingSocks.udp = self.enableUdp
|
||
if self.enableSniffing {
|
||
inSocks.sniffing = V2rayInboundSniffing()
|
||
}
|
||
|
||
if self.httpPort == self.socksPort {
|
||
self.httpPort = String((Int(self.socksPort) ?? 0) + 1)
|
||
}
|
||
|
||
if self.isNewVersion {
|
||
// inbounds
|
||
var inbounds: [V2rayInbound] = [inSocks, inHttp]
|
||
if (!self.isEmptyInput && self.v2ray.inbounds != nil && self.v2ray.inbounds!.count > 0) {
|
||
for (_, item) in self.v2ray.inbounds!.enumerated() {
|
||
if item.protocol == V2rayProtocolInbound.http || item.protocol == V2rayProtocolInbound.socks {
|
||
continue
|
||
}
|
||
inbounds.append(item)
|
||
}
|
||
self.v2ray.inbounds = inbounds
|
||
} else {
|
||
// new add
|
||
self.v2ray.inbounds = inbounds
|
||
}
|
||
self.v2ray.inboundDetour = nil
|
||
self.v2ray.inbound = nil
|
||
} else {
|
||
self.v2ray.inbounds = nil
|
||
// inbound
|
||
var inType: V2rayProtocolInbound = V2rayProtocolInbound.socks
|
||
if self.v2ray.inbound != nil {
|
||
if self.v2ray.inbound!.protocol == V2rayProtocolInbound.http {
|
||
self.v2ray.inbound!.port = self.httpPort
|
||
self.v2ray.inbound!.listen = self.httpHost
|
||
inType = V2rayProtocolInbound.http
|
||
}
|
||
if self.v2ray.inbound!.protocol == V2rayProtocolInbound.socks {
|
||
|
||
self.v2ray.inbound!.port = self.socksPort
|
||
self.v2ray.inbound!.listen = self.socksHost
|
||
self.v2ray.inbound!.settingSocks.udp = self.enableUdp
|
||
}
|
||
} else {
|
||
self.v2ray.inbound = inSocks
|
||
}
|
||
|
||
// inboundDetour
|
||
if (self.v2ray.inboundDetour != nil && self.v2ray.inboundDetour!.count > 0) {
|
||
var inboundDetour: [V2rayInbound] = []
|
||
|
||
for var (_, item) in self.v2ray.inboundDetour!.enumerated() {
|
||
if self.enableSniffing {
|
||
item.sniffing = V2rayInboundSniffing()
|
||
}
|
||
if inType == V2rayProtocolInbound.http && item.protocol == V2rayProtocolInbound.http {
|
||
continue
|
||
}
|
||
if inType == V2rayProtocolInbound.socks && item.protocol == V2rayProtocolInbound.socks {
|
||
continue
|
||
}
|
||
inboundDetour.append(item)
|
||
}
|
||
|
||
self.v2ray.inboundDetour = inboundDetour
|
||
} else {
|
||
if inType == V2rayProtocolInbound.http {
|
||
self.v2ray.inboundDetour = [inSocks]
|
||
}
|
||
if inType == V2rayProtocolInbound.socks {
|
||
self.v2ray.inboundDetour = [inHttp]
|
||
}
|
||
}
|
||
}
|
||
// ------------------------------------- inbound end ----------------------------------------------
|
||
|
||
// ------------------------------------- outbound start -------------------------------------------
|
||
// outbound Freedom
|
||
var outboundFreedom = V2rayOutbound()
|
||
outboundFreedom.protocol = V2rayProtocolOutbound.freedom
|
||
outboundFreedom.tag = "direct"
|
||
outboundFreedom.settingFreedom = V2rayOutboundFreedom()
|
||
|
||
// outbound Blackhole
|
||
var outboundBlackhole = V2rayOutbound()
|
||
outboundBlackhole.protocol = V2rayProtocolOutbound.blackhole
|
||
outboundBlackhole.tag = "block"
|
||
outboundBlackhole.settingBlackhole = V2rayOutboundBlackhole()
|
||
|
||
// outbound
|
||
let outbound = self.getOutbound()
|
||
|
||
if self.isEmptyInput {
|
||
if self.isNewVersion {
|
||
self.v2ray.outbounds = [outbound, outboundFreedom, outboundBlackhole]
|
||
self.v2ray.outbound = nil
|
||
self.v2ray.outboundDetour = nil
|
||
} else {
|
||
self.v2ray.outbounds = nil
|
||
self.v2ray.outbound = outbound
|
||
self.v2ray.outboundDetour = [outboundFreedom, outboundBlackhole]
|
||
}
|
||
} else {
|
||
if self.isNewVersion {
|
||
if (self.v2ray.outbounds != nil && self.v2ray.outbounds!.count > 0) {
|
||
var outbounds: [V2rayOutbound] = []
|
||
for var (_, item) in self.v2ray.outbounds!.enumerated() {
|
||
if self.serverProtocol == item.protocol.rawValue {
|
||
// replace data
|
||
item = self.replaceOutbound(item: item)
|
||
}
|
||
outbounds.append(item)
|
||
}
|
||
self.v2ray.outbounds = outbounds
|
||
} else {
|
||
self.v2ray.outbounds = [outbound, outboundFreedom, outboundBlackhole]
|
||
}
|
||
self.v2ray.outboundDetour = nil
|
||
self.v2ray.outbound = nil
|
||
} else {
|
||
// if has outbounds
|
||
self.v2ray.outbounds = nil
|
||
|
||
// outbound
|
||
if self.v2ray.outbound != nil {
|
||
self.v2ray.outbound = self.replaceOutbound(item: self.v2ray.outbound!)
|
||
} else {
|
||
self.v2ray.outbound = outbound
|
||
}
|
||
|
||
// outboundDetour
|
||
if !(self.v2ray.outboundDetour != nil && self.v2ray.outboundDetour!.count > 0) {
|
||
self.v2ray.outboundDetour = [outboundFreedom, outboundBlackhole]
|
||
}
|
||
}
|
||
}
|
||
|
||
// ------------------------------------- outbound end ---------------------------------------------
|
||
|
||
// ------------------------------------- routing start --------------------------------------------
|
||
|
||
self.routing.settings.domainStrategy = self.routingDomainStrategy
|
||
var rules: [V2rayRoutingSettingRule] = []
|
||
|
||
// proxy
|
||
var ruleProxyDomain, ruleProxyIp, ruleDirectDomain, ruleDirectIp, ruleBlockDomain, ruleBlockIp: V2rayRoutingSettingRule?
|
||
if self.routingProxyDomains.count > 0 {
|
||
ruleProxyDomain = V2rayRoutingSettingRule()
|
||
// tag is proxy
|
||
ruleProxyDomain?.outboundTag = "proxy"
|
||
ruleProxyDomain?.domain = self.routingProxyDomains
|
||
}
|
||
if self.routingProxyIps.count > 0 {
|
||
ruleProxyIp = V2rayRoutingSettingRule()
|
||
// tag is proxy
|
||
ruleProxyIp?.outboundTag = "proxy"
|
||
ruleProxyIp?.ip = self.routingProxyIps
|
||
}
|
||
|
||
// direct
|
||
if self.routingDirectDomains.count > 0 {
|
||
ruleDirectDomain = V2rayRoutingSettingRule()
|
||
// tag is proxy
|
||
ruleDirectDomain?.outboundTag = "direct"
|
||
ruleDirectDomain?.domain = self.routingDirectDomains
|
||
}
|
||
if self.routingDirectIps.count > 0 {
|
||
ruleDirectIp = V2rayRoutingSettingRule()
|
||
// tag is proxy
|
||
ruleDirectIp?.outboundTag = "direct"
|
||
ruleDirectIp?.ip = self.routingDirectIps
|
||
}
|
||
// block
|
||
if self.routingBlockDomains.count > 0 {
|
||
ruleBlockDomain = V2rayRoutingSettingRule()
|
||
// tag is proxy
|
||
ruleBlockDomain?.outboundTag = "block"
|
||
ruleBlockDomain?.domain = self.routingBlockDomains
|
||
}
|
||
|
||
if self.routingBlockIps.count > 0 {
|
||
ruleBlockIp = V2rayRoutingSettingRule()
|
||
// tag is proxy
|
||
ruleBlockIp?.outboundTag = "block"
|
||
ruleBlockIp?.domain = self.routingBlockDomains
|
||
ruleBlockIp?.ip = self.routingBlockIps
|
||
}
|
||
|
||
// default
|
||
if ruleDirectDomain == nil {
|
||
ruleDirectDomain = V2rayRoutingSettingRule()
|
||
// tag is proxy
|
||
ruleDirectDomain?.outboundTag = "direct"
|
||
}
|
||
|
||
if ruleDirectIp == nil {
|
||
ruleDirectIp = V2rayRoutingSettingRule()
|
||
// tag is proxy
|
||
ruleDirectIp?.outboundTag = "direct"
|
||
}
|
||
|
||
switch self.routingRule {
|
||
case .RoutingRuleGlobal:
|
||
// all set to nil
|
||
// (ruleProxyDomain, ruleProxyIp, ruleDirectDomain, ruleDirectIp, ruleBlockDomain, ruleBlockIp) = (nil, nil, nil, nil, nil, nil)
|
||
if ruleDirectDomain?.domain?.count == 0 {
|
||
ruleDirectDomain = nil
|
||
}
|
||
if ruleDirectIp?.ip?.count == 0 {
|
||
ruleDirectIp = nil
|
||
}
|
||
break
|
||
case .RoutingRuleLAN:
|
||
ruleDirectIp?.ip?.append("geoip:private")
|
||
ruleDirectDomain?.domain?.append("localhost")
|
||
|
||
case .RoutingRuleCn:
|
||
ruleDirectIp?.ip?.append("geoip:cn")
|
||
ruleDirectDomain?.domain?.append("geosite:cn")
|
||
|
||
case .RoutingRuleLANAndCn:
|
||
ruleDirectIp?.ip?.append("geoip:private")
|
||
ruleDirectIp?.ip?.append("geoip:cn")
|
||
ruleDirectDomain?.domain?.append("localhost")
|
||
ruleDirectDomain?.domain?.append("geosite:cn")
|
||
}
|
||
|
||
if ruleProxyDomain != nil {
|
||
ruleProxyDomain?.ip = nil
|
||
rules.append(ruleProxyDomain!)
|
||
}
|
||
if ruleProxyIp != nil {
|
||
ruleProxyIp?.domain = nil
|
||
rules.append(ruleProxyIp!)
|
||
}
|
||
if ruleDirectDomain != nil {
|
||
ruleDirectDomain!.ip = nil
|
||
rules.append(ruleDirectDomain!)
|
||
}
|
||
if ruleDirectIp != nil {
|
||
ruleDirectIp!.domain = nil
|
||
rules.append(ruleDirectIp!)
|
||
}
|
||
if ruleBlockDomain != nil {
|
||
ruleBlockDomain?.ip = nil
|
||
rules.append(ruleBlockDomain!)
|
||
}
|
||
if ruleBlockIp != nil {
|
||
ruleBlockIp?.domain = nil
|
||
rules.append(ruleBlockIp!)
|
||
}
|
||
|
||
self.routing.settings.rules = rules
|
||
// set v2ray routing
|
||
self.v2ray.routing = self.routing
|
||
// ------------------------------------- routing end ----------------------------------------------
|
||
}
|
||
|
||
private func replaceOutbound(item: V2rayOutbound) -> V2rayOutbound {
|
||
var outbound = item
|
||
switch outbound.protocol {
|
||
case V2rayProtocolOutbound.vmess:
|
||
var vmess = outbound.settingVMess
|
||
if vmess == nil {
|
||
vmess = V2rayOutboundVMess()
|
||
}
|
||
vmess!.vnext = [self.serverVmess]
|
||
outbound.settingVMess = vmess
|
||
|
||
// enable mux only vmess
|
||
var mux = V2rayOutboundMux()
|
||
mux.enabled = self.enableMux
|
||
mux.concurrency = self.mux
|
||
|
||
outbound.mux = mux
|
||
|
||
break
|
||
case V2rayProtocolOutbound.shadowsocks:
|
||
var ss = outbound.settingShadowsocks
|
||
if ss == nil {
|
||
ss = V2rayOutboundShadowsocks()
|
||
}
|
||
ss!.servers = [self.serverShadowsocks]
|
||
outbound.settingShadowsocks = ss
|
||
break
|
||
case V2rayProtocolOutbound.socks:
|
||
print("self.serverSocks5", self.serverSocks5)
|
||
outbound.settingSocks = self.serverSocks5
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
|
||
outbound.streamSettings = self.getStreamSettings()
|
||
return outbound
|
||
}
|
||
|
||
func checkManualValid() {
|
||
defer {
|
||
if self.error != "" {
|
||
self.isValid = false
|
||
} else {
|
||
self.isValid = true
|
||
}
|
||
}
|
||
// reset error first
|
||
self.error = ""
|
||
// check main outbound
|
||
switch self.serverProtocol {
|
||
case V2rayProtocolOutbound.vmess.rawValue:
|
||
if self.serverVmess.address.count == 0 {
|
||
self.error = "missing vmess.address";
|
||
return
|
||
}
|
||
if self.serverVmess.port == 0 {
|
||
self.error = "missing vmess.port";
|
||
return
|
||
}
|
||
if self.serverVmess.users.count > 0 {
|
||
if self.serverVmess.users[0].id.count == 0 {
|
||
self.error = "missing vmess.users[0].id";
|
||
return
|
||
}
|
||
} else {
|
||
self.error = "missing vmess.users";
|
||
return
|
||
}
|
||
break
|
||
case V2rayProtocolOutbound.shadowsocks.rawValue:
|
||
if self.serverShadowsocks.address.count == 0 {
|
||
self.error = "missing shadowsocks.address";
|
||
return
|
||
}
|
||
if self.serverShadowsocks.port == 0 {
|
||
self.error = "missing shadowsocks.port";
|
||
return
|
||
}
|
||
if self.serverShadowsocks.password.count == 0 {
|
||
self.error = "missing shadowsocks.password";
|
||
return
|
||
}
|
||
if self.serverShadowsocks.method.count == 0 {
|
||
self.error = "missing shadowsocks.method";
|
||
return
|
||
}
|
||
break
|
||
case V2rayProtocolOutbound.socks.rawValue:
|
||
if self.serverSocks5.servers[0].address.count == 0 {
|
||
self.error = "missing socks.address";
|
||
return
|
||
}
|
||
if self.serverSocks5.servers[0].port == 0 {
|
||
self.error = "missing socks.port";
|
||
return
|
||
}
|
||
break
|
||
default:
|
||
self.error = "missing outbound.protocol";
|
||
return
|
||
}
|
||
|
||
// check stream setting
|
||
switch self.streamNetwork {
|
||
case V2rayStreamSettings.network.h2.rawValue:
|
||
break
|
||
case V2rayStreamSettings.network.ws.rawValue:
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
}
|
||
|
||
private func getOutbound() -> V2rayOutbound {
|
||
var outbound = V2rayOutbound()
|
||
outbound.protocol = V2rayProtocolOutbound(rawValue: self.serverProtocol)!
|
||
outbound.tag = "proxy"
|
||
|
||
switch outbound.protocol {
|
||
case V2rayProtocolOutbound.vmess:
|
||
var vmess = V2rayOutboundVMess()
|
||
vmess.vnext = [self.serverVmess]
|
||
outbound.settingVMess = vmess
|
||
|
||
// enable mux only vmess
|
||
var mux = V2rayOutboundMux()
|
||
mux.enabled = self.enableMux
|
||
mux.concurrency = self.mux
|
||
outbound.mux = mux
|
||
|
||
break
|
||
case V2rayProtocolOutbound.shadowsocks:
|
||
var ss = V2rayOutboundShadowsocks()
|
||
ss.servers = [self.serverShadowsocks]
|
||
outbound.settingShadowsocks = ss
|
||
break
|
||
case V2rayProtocolOutbound.socks:
|
||
outbound.settingSocks = self.serverSocks5
|
||
break
|
||
default:
|
||
break
|
||
}
|
||
|
||
outbound.streamSettings = self.getStreamSettings()
|
||
|
||
return outbound
|
||
}
|
||
|
||
private func getStreamSettings() -> V2rayStreamSettings {
|
||
// streamSettings
|
||
var streamSettings = V2rayStreamSettings()
|
||
streamSettings.network = V2rayStreamSettings.network(rawValue: self.streamNetwork) ?? V2rayStreamSettings.network.tcp
|
||
switch streamSettings.network {
|
||
case .tcp:
|
||
streamSettings.tcpSettings = self.streamTcp
|
||
break
|
||
case .kcp:
|
||
streamSettings.kcpSettings = self.streamKcp
|
||
break
|
||
case .http, .h2:
|
||
streamSettings.httpSettings = self.streamH2
|
||
break
|
||
case .ws:
|
||
streamSettings.wsSettings = self.streamWs
|
||
break
|
||
case .domainsocket:
|
||
streamSettings.dsSettings = self.streamDs
|
||
break
|
||
case .quic:
|
||
streamSettings.quicSettings = self.streamQuic
|
||
break
|
||
}
|
||
streamSettings.security = self.streamTlsSecurity == "tls" ? .tls : .none
|
||
var tls = TlsSettings()
|
||
|
||
tls.allowInsecure = self.streamTlsAllowInsecure
|
||
if self.streamTlsServerName.count > 0 {
|
||
tls.serverName = self.streamTlsServerName
|
||
}
|
||
|
||
streamSettings.tlsSettings = tls
|
||
|
||
return streamSettings
|
||
}
|
||
|
||
// parse imported or edited json text
|
||
// by import tab view
|
||
func parseJson(jsonText: String) {
|
||
defer {
|
||
if self.errors.count > 0 {
|
||
self.isValid = false
|
||
} else {
|
||
self.isValid = true
|
||
}
|
||
}
|
||
|
||
self.errors = []
|
||
|
||
guard var json = try? JSON(data: jsonText.data(using: String.Encoding.utf8, allowLossyConversion: false)!) else {
|
||
self.errors += ["invalid json"]
|
||
return
|
||
}
|
||
|
||
if !json.exists() {
|
||
self.errors += ["invalid json"]
|
||
return
|
||
}
|
||
|
||
// ignore dns, use default
|
||
|
||
// ============ parse inbound start =========================================
|
||
// > 4.0
|
||
if json["inbounds"].exists() {
|
||
self.isNewVersion = true
|
||
// check inbounds
|
||
if json["inbounds"].arrayValue.count > 0 {
|
||
var inbounds: [V2rayInbound] = []
|
||
json["inbounds"].arrayValue.forEach {
|
||
val in
|
||
inbounds += [self.parseInbound(jsonParams: val)]
|
||
}
|
||
self.v2ray.inbounds = inbounds
|
||
} else {
|
||
self.error = "missing inbounds"
|
||
}
|
||
} else {
|
||
// old version
|
||
// 1. inbound
|
||
if json["inbound"].dictionaryValue.count > 0 {
|
||
self.v2ray.inbound = self.parseInbound(jsonParams: json["inbound"])
|
||
} else {
|
||
self.error = "missing inbound"
|
||
}
|
||
|
||
// 2. inboundDetour
|
||
if json["inboundDetour"].arrayValue.count > 0 {
|
||
var inboundDetour: [V2rayInbound] = []
|
||
json["inboundDetour"].arrayValue.forEach {
|
||
val in
|
||
inboundDetour += [self.parseInbound(jsonParams: val)]
|
||
}
|
||
self.v2ray.inboundDetour = inboundDetour
|
||
}
|
||
}
|
||
// ------------ parse inbound end -------------------------------------------
|
||
|
||
// ============ parse outbound start =========================================
|
||
// > 4.0
|
||
if json["outbounds"].exists() {
|
||
self.isNewVersion = true
|
||
// check outbounds
|
||
if json["outbounds"].arrayValue.count > 0 {
|
||
// outbounds
|
||
var outbounds: [V2rayOutbound] = []
|
||
json["outbounds"].arrayValue.forEach {
|
||
val in
|
||
outbounds += [self.parseOutbound(jsonParams: val)]
|
||
}
|
||
self.v2ray.outbounds = outbounds
|
||
} else {
|
||
self.errors += ["missing outbounds"]
|
||
}
|
||
} else {
|
||
// check outbounds
|
||
// 1. outbound
|
||
if json["outbound"].dictionaryValue.count > 0 {
|
||
self.v2ray.outbound = self.parseOutbound(jsonParams: json["outbound"])
|
||
} else {
|
||
self.errors += ["missing outbound"]
|
||
}
|
||
|
||
// outboundDetour
|
||
if json["outboundDetour"].arrayValue.count > 0 {
|
||
var outboundDetour: [V2rayOutbound] = []
|
||
|
||
json["outboundDetour"].arrayValue.forEach {
|
||
val in
|
||
outboundDetour += [self.parseOutbound(jsonParams: val)]
|
||
}
|
||
|
||
self.v2ray.outboundDetour = outboundDetour
|
||
}
|
||
}
|
||
// ------------ parse outbound end -------------------------------------------
|
||
|
||
v2ray.transport = self.parseTransport(steamJson: json["transport"])
|
||
}
|
||
|
||
// parse inbound from json
|
||
func parseInbound(jsonParams: JSON) -> (V2rayInbound) {
|
||
var v2rayInbound = V2rayInbound()
|
||
|
||
if !jsonParams["protocol"].exists() {
|
||
self.errors += ["missing inbound.protocol"]
|
||
return (v2rayInbound)
|
||
}
|
||
|
||
if (V2rayProtocolInbound(rawValue: jsonParams["protocol"].stringValue) == nil) {
|
||
self.errors += ["invalid inbound.protocol"]
|
||
return (v2rayInbound)
|
||
}
|
||
|
||
// set protocol
|
||
v2rayInbound.protocol = V2rayProtocolInbound(rawValue: jsonParams["protocol"].stringValue)!
|
||
|
||
if !jsonParams["port"].exists() {
|
||
self.errors += ["missing inbound.port"]
|
||
}
|
||
|
||
if !(jsonParams["port"].intValue > 1024 && jsonParams["port"].intValue < 65535) {
|
||
self.errors += ["invalid inbound.port"]
|
||
}
|
||
|
||
// set port
|
||
v2rayInbound.port = String(jsonParams["port"].intValue)
|
||
|
||
if jsonParams["listen"].stringValue.count > 0 {
|
||
// set listen
|
||
v2rayInbound.listen = jsonParams["listen"].stringValue
|
||
}
|
||
|
||
if jsonParams["tag"].stringValue.count > 0 {
|
||
// set tag
|
||
v2rayInbound.tag = jsonParams["tag"].stringValue
|
||
}
|
||
|
||
// settings depends on protocol
|
||
if jsonParams["settings"].dictionaryValue.count > 0 {
|
||
|
||
switch v2rayInbound.protocol {
|
||
|
||
case .http:
|
||
var settings = V2rayInboundHttp()
|
||
|
||
if jsonParams["settings"]["timeout"].dictionaryValue.count > 0 {
|
||
settings.timeout = jsonParams["settings"]["timeout"].intValue
|
||
}
|
||
|
||
if jsonParams["settings"]["allowTransparent"].dictionaryValue.count > 0 {
|
||
settings.allowTransparent = jsonParams["settings"]["allowTransparent"].boolValue
|
||
}
|
||
|
||
if jsonParams["settings"]["userLevel"].dictionaryValue.count > 0 {
|
||
settings.userLevel = jsonParams["settings"]["userLevel"].intValue
|
||
}
|
||
// accounts
|
||
if jsonParams["settings"]["accounts"].dictionaryValue.count > 0 {
|
||
var accounts: [V2rayInboundHttpAccount] = []
|
||
for subJson in jsonParams["settings"]["accounts"].arrayValue {
|
||
var account = V2rayInboundHttpAccount()
|
||
account.user = subJson["user"].stringValue
|
||
account.pass = subJson["pass"].stringValue
|
||
accounts.append(account)
|
||
}
|
||
settings.accounts = accounts
|
||
}
|
||
// use default setting
|
||
v2rayInbound.port = self.httpPort
|
||
v2rayInbound.port = self.httpHost
|
||
// set into inbound
|
||
v2rayInbound.settingHttp = settings
|
||
break
|
||
|
||
case .shadowsocks:
|
||
var settings = V2rayInboundShadowsocks()
|
||
settings.email = jsonParams["settings"]["timeout"].stringValue
|
||
settings.password = jsonParams["settings"]["password"].stringValue
|
||
settings.method = jsonParams["settings"]["method"].stringValue
|
||
if V2rayOutboundShadowsockMethod.firstIndex(of: jsonParams["settings"]["method"].stringValue) != nil {
|
||
settings.method = jsonParams["settings"]["method"].stringValue
|
||
} else {
|
||
settings.method = V2rayOutboundShadowsockMethod[0]
|
||
}
|
||
settings.udp = jsonParams["settings"]["udp"].boolValue
|
||
settings.level = jsonParams["settings"]["level"].intValue
|
||
settings.ota = jsonParams["settings"]["ota"].boolValue
|
||
|
||
// set into inbound
|
||
v2rayInbound.settingShadowsocks = settings
|
||
break
|
||
|
||
case .socks:
|
||
var settings = V2rayInboundSocks()
|
||
settings.auth = jsonParams["settings"]["auth"].stringValue
|
||
// accounts
|
||
if jsonParams["settings"]["accounts"].dictionaryValue.count > 0 {
|
||
var accounts: [V2rayInboundSockAccount] = []
|
||
for subJson in jsonParams["settings"]["accounts"].arrayValue {
|
||
var account = V2rayInboundSockAccount()
|
||
account.user = subJson["user"].stringValue
|
||
account.pass = subJson["pass"].stringValue
|
||
accounts.append(account)
|
||
}
|
||
settings.accounts = accounts
|
||
}
|
||
|
||
settings.udp = jsonParams["settings"]["udp"].boolValue
|
||
settings.ip = jsonParams["settings"]["ip"].stringValue
|
||
settings.userLevel = jsonParams["settings"]["userLevel"].intValue
|
||
|
||
self.enableUdp = jsonParams["settings"]["udp"].boolValue
|
||
// use default setting
|
||
settings.udp = self.enableUdp
|
||
v2rayInbound.port = self.socksPort
|
||
v2rayInbound.listen = self.socksHost
|
||
// set into inbound
|
||
v2rayInbound.settingSocks = settings
|
||
break
|
||
|
||
case .vmess:
|
||
var settings = V2rayInboundVMess()
|
||
settings.disableInsecureEncryption = jsonParams["settings"]["disableInsecureEncryption"].boolValue
|
||
// clients
|
||
if jsonParams["settings"]["clients"].dictionaryValue.count > 0 {
|
||
var clients: [V2RayInboundVMessClient] = []
|
||
for subJson in jsonParams["settings"]["clients"].arrayValue {
|
||
var client = V2RayInboundVMessClient()
|
||
client.id = subJson["id"].stringValue
|
||
client.level = subJson["level"].intValue
|
||
client.alterId = subJson["alterId"].intValue
|
||
client.email = subJson["email"].stringValue
|
||
clients.append(client)
|
||
}
|
||
settings.clients = clients
|
||
}
|
||
|
||
if jsonParams["settings"]["default"].dictionaryValue.count > 0 {
|
||
settings.`default`?.level = jsonParams["settings"]["default"]["level"].intValue
|
||
settings.`default`?.alterId = jsonParams["settings"]["default"]["alterId"].intValue
|
||
}
|
||
|
||
if jsonParams["settings"]["detour"].dictionaryValue.count > 0 {
|
||
var detour = V2RayInboundVMessDetour()
|
||
detour.to = jsonParams["settings"]["detour"]["to"].stringValue
|
||
settings.detour = detour
|
||
}
|
||
|
||
// set into inbound
|
||
v2rayInbound.settingVMess = settings
|
||
break
|
||
}
|
||
}
|
||
|
||
// stream settings
|
||
if jsonParams["streamSettings"].dictionaryValue.count > 0 {
|
||
v2rayInbound.streamSettings = self.parseSteamSettings(steamJson: jsonParams["streamSettings"], preTxt: "inbound")
|
||
}
|
||
|
||
return (v2rayInbound)
|
||
}
|
||
|
||
// parse outbound from json
|
||
func parseOutbound(jsonParams: JSON) -> (V2rayOutbound) {
|
||
var v2rayOutbound = V2rayOutbound()
|
||
|
||
if !(jsonParams["protocol"].exists()) {
|
||
self.errors += ["missing outbound.protocol"]
|
||
return (v2rayOutbound)
|
||
}
|
||
|
||
if (V2rayProtocolOutbound(rawValue: jsonParams["protocol"].stringValue) == nil) {
|
||
self.errors += ["invalid outbound.protocol"]
|
||
return (v2rayOutbound)
|
||
}
|
||
|
||
// set protocol
|
||
v2rayOutbound.protocol = V2rayProtocolOutbound(rawValue: jsonParams["protocol"].stringValue)!
|
||
|
||
v2rayOutbound.sendThrough = jsonParams["sendThrough"].stringValue
|
||
|
||
// fix Outbound tag
|
||
switch v2rayOutbound.protocol {
|
||
case .freedom:
|
||
v2rayOutbound.tag = "direct"
|
||
case .blackhole:
|
||
v2rayOutbound.tag = "block"
|
||
default:
|
||
v2rayOutbound.tag = "proxy"
|
||
}
|
||
|
||
// settings depends on protocol
|
||
if jsonParams["settings"].dictionaryValue.count > 0 {
|
||
switch v2rayOutbound.protocol {
|
||
case .blackhole:
|
||
var settingBlackhole = V2rayOutboundBlackhole()
|
||
settingBlackhole.response.type = jsonParams["settings"]["response"]["type"].stringValue
|
||
// set into outbound
|
||
v2rayOutbound.settingBlackhole = settingBlackhole
|
||
break
|
||
|
||
case .freedom:
|
||
var settingFreedom = V2rayOutboundFreedom()
|
||
settingFreedom.domainStrategy = jsonParams["settings"]["domainStrategy"].stringValue
|
||
settingFreedom.userLevel = jsonParams["settings"]["userLevel"].intValue
|
||
settingFreedom.redirect = jsonParams["settings"]["redirect"].stringValue
|
||
// set into outbound
|
||
v2rayOutbound.settingFreedom = settingFreedom
|
||
break
|
||
|
||
case .dns:
|
||
var settingDns = V2rayOutboundDns()
|
||
settingDns.network = jsonParams["settings"]["network"].stringValue
|
||
settingDns.address = jsonParams["settings"]["address"].stringValue
|
||
settingDns.port = jsonParams["settings"]["port"].intValue
|
||
// set into outbound
|
||
v2rayOutbound.settingDns = settingDns
|
||
break
|
||
|
||
case .http:
|
||
var settingHttp = V2rayOutboundHttp()
|
||
var servers: [V2rayOutboundHttpServer] = []
|
||
|
||
jsonParams["settings"]["servers"].arrayValue.forEach {
|
||
val in
|
||
var server = V2rayOutboundHttpServer()
|
||
server.port = val["port"].intValue
|
||
server.address = val["address"].stringValue
|
||
|
||
var users: [V2rayOutboundHttpUser] = []
|
||
val["users"].arrayValue.forEach {
|
||
val in
|
||
var user = V2rayOutboundHttpUser()
|
||
user.user = val["user"].stringValue
|
||
user.pass = val["pass"].stringValue
|
||
// append
|
||
users.append(user)
|
||
}
|
||
|
||
server.users = users
|
||
// append
|
||
servers.append(server)
|
||
}
|
||
|
||
settingHttp.servers = servers
|
||
|
||
// set into outbound
|
||
v2rayOutbound.settingHttp = settingHttp
|
||
|
||
break
|
||
|
||
case .shadowsocks:
|
||
var settingShadowsocks = V2rayOutboundShadowsocks()
|
||
var servers: [V2rayOutboundShadowsockServer] = []
|
||
// servers
|
||
jsonParams["settings"]["servers"].arrayValue.forEach {
|
||
val in
|
||
var server = V2rayOutboundShadowsockServer()
|
||
server.port = val["port"].intValue
|
||
server.email = val["email"].stringValue
|
||
server.address = val["address"].stringValue
|
||
|
||
if V2rayOutboundShadowsockMethod.firstIndex(of: val["method"].stringValue) != nil {
|
||
server.method = val["method"].stringValue
|
||
} else {
|
||
server.method = V2rayOutboundShadowsockMethod[0]
|
||
}
|
||
|
||
server.password = val["password"].stringValue
|
||
server.ota = val["ota"].boolValue
|
||
server.level = val["level"].intValue
|
||
// append
|
||
servers.append(server)
|
||
}
|
||
settingShadowsocks.servers = servers
|
||
// set into outbound
|
||
v2rayOutbound.settingShadowsocks = settingShadowsocks
|
||
break
|
||
|
||
case .socks:
|
||
var settingSocks = V2rayOutboundSocks()
|
||
var servers: [V2rayOutboundSockServer] = []
|
||
|
||
jsonParams["settings"]["servers"].arrayValue.forEach {
|
||
val in
|
||
var server = V2rayOutboundSockServer()
|
||
server.port = val["port"].intValue
|
||
server.address = val["address"].stringValue
|
||
|
||
var users: [V2rayOutboundSockUser] = []
|
||
val["users"].arrayValue.forEach {
|
||
val in
|
||
var user = V2rayOutboundSockUser()
|
||
user.user = val["user"].stringValue
|
||
user.pass = val["pass"].stringValue
|
||
user.level = val["level"].intValue
|
||
// append
|
||
users.append(user)
|
||
}
|
||
|
||
server.users = users
|
||
// append
|
||
servers.append(server)
|
||
}
|
||
|
||
settingSocks.servers = servers
|
||
|
||
// set into outbound
|
||
v2rayOutbound.settingSocks = settingSocks
|
||
break
|
||
|
||
case .vmess:
|
||
var settingVMess = V2rayOutboundVMess()
|
||
var vnext: [V2rayOutboundVMessItem] = []
|
||
|
||
jsonParams["settings"]["vnext"].arrayValue.forEach {
|
||
val in
|
||
var item = V2rayOutboundVMessItem()
|
||
|
||
item.address = val["address"].stringValue
|
||
item.port = val["port"].intValue
|
||
|
||
var users: [V2rayOutboundVMessUser] = []
|
||
val["users"].arrayValue.forEach {
|
||
val in
|
||
var user = V2rayOutboundVMessUser()
|
||
user.id = val["id"].stringValue
|
||
user.alterId = val["alterId"].intValue
|
||
user.level = val["level"].intValue
|
||
if V2rayOutboundVMessSecurity.firstIndex(of: val["security"].stringValue) != nil {
|
||
user.security = val["security"].stringValue
|
||
}
|
||
users.append(user)
|
||
}
|
||
item.users = users
|
||
// append
|
||
vnext.append(item)
|
||
}
|
||
|
||
settingVMess.vnext = vnext
|
||
|
||
// set into outbound
|
||
v2rayOutbound.settingVMess = settingVMess
|
||
|
||
// enable mux only vmess
|
||
var mux = V2rayOutboundMux()
|
||
mux.enabled = self.enableMux
|
||
mux.concurrency = self.mux
|
||
v2rayOutbound.mux = mux
|
||
|
||
break
|
||
}
|
||
}
|
||
|
||
// stream settings
|
||
if jsonParams["streamSettings"].dictionaryValue.count > 0 {
|
||
v2rayOutbound.streamSettings = self.parseSteamSettings(steamJson: jsonParams["streamSettings"], preTxt: "outbound")
|
||
}
|
||
|
||
// set main server protocol
|
||
if !self.foundServerProtocol && [V2rayProtocolOutbound.socks.rawValue, V2rayProtocolOutbound.vmess.rawValue, V2rayProtocolOutbound.shadowsocks.rawValue].contains(v2rayOutbound.protocol.rawValue) {
|
||
self.serverProtocol = v2rayOutbound.protocol.rawValue
|
||
self.foundServerProtocol = true
|
||
}
|
||
|
||
if v2rayOutbound.protocol == V2rayProtocolOutbound.socks {
|
||
self.serverSocks5 = v2rayOutbound.settingSocks!
|
||
}
|
||
|
||
if v2rayOutbound.protocol == V2rayProtocolOutbound.vmess && v2rayOutbound.settingVMess != nil && v2rayOutbound.settingVMess!.vnext.count > 0 {
|
||
self.serverVmess = v2rayOutbound.settingVMess!.vnext[0]
|
||
}
|
||
|
||
if v2rayOutbound.protocol == V2rayProtocolOutbound.shadowsocks && v2rayOutbound.settingShadowsocks != nil && v2rayOutbound.settingShadowsocks!.servers.count > 0 {
|
||
self.serverShadowsocks = v2rayOutbound.settingShadowsocks!.servers[0]
|
||
}
|
||
|
||
return (v2rayOutbound)
|
||
}
|
||
|
||
// parse steamSettings
|
||
func parseSteamSettings(steamJson: JSON, preTxt: String = "") -> (V2rayStreamSettings) {
|
||
var stream = V2rayStreamSettings()
|
||
|
||
if (V2rayStreamSettings.network(rawValue: steamJson["network"].stringValue) == nil) {
|
||
self.errors += ["invalid " + preTxt + ".streamSettings.network"]
|
||
} else {
|
||
// set network
|
||
stream.network = V2rayStreamSettings.network(rawValue: steamJson["network"].stringValue)!
|
||
self.streamNetwork = stream.network.rawValue
|
||
}
|
||
|
||
if (V2rayStreamSettings.security(rawValue: steamJson["security"].stringValue) == nil) {
|
||
self.errors += ["invalid " + preTxt + ".streamSettings.security"]
|
||
} else {
|
||
// set security
|
||
stream.security = V2rayStreamSettings.security(rawValue: steamJson["security"].stringValue)!
|
||
self.streamTlsSecurity = stream.security.rawValue
|
||
}
|
||
|
||
if steamJson["sockopt"].dictionaryValue.count > 0 {
|
||
var sockopt = V2rayStreamSettingSockopt()
|
||
|
||
// tproxy
|
||
if (V2rayStreamSettingSockopt.tproxy(rawValue: steamJson["sockopt"]["tproxy"].stringValue) != nil) {
|
||
sockopt.tproxy = V2rayStreamSettingSockopt.tproxy(rawValue: steamJson["sockopt"]["tproxy"].stringValue)!
|
||
}
|
||
|
||
sockopt.tcpFastOpen = steamJson["sockopt"]["tcpFastOpen"].boolValue
|
||
sockopt.mark = steamJson["sockopt"]["mark"].intValue
|
||
|
||
stream.sockopt = sockopt
|
||
}
|
||
|
||
// steamSettings (same as global transport)
|
||
let transport = self.parseTransport(steamJson: steamJson)
|
||
stream.tlsSettings = transport.tlsSettings
|
||
stream.tcpSettings = transport.tcpSettings
|
||
stream.kcpSettings = transport.kcpSettings
|
||
stream.wsSettings = transport.wsSettings
|
||
stream.httpSettings = transport.httpSettings
|
||
stream.dsSettings = transport.dsSettings
|
||
|
||
// for outbound stream
|
||
if preTxt == "outbound" {
|
||
if transport.tlsSettings != nil {
|
||
// set data
|
||
if transport.tlsSettings?.serverName != nil {
|
||
self.streamTlsServerName = transport.tlsSettings!.serverName!
|
||
}
|
||
if transport.tlsSettings?.serverName != nil {
|
||
self.streamTlsAllowInsecure = transport.tlsSettings!.allowInsecure!
|
||
}
|
||
}
|
||
|
||
if transport.tcpSettings != nil {
|
||
self.streamTcp = transport.tcpSettings!
|
||
}
|
||
|
||
if transport.kcpSettings != nil {
|
||
self.streamKcp = transport.kcpSettings!
|
||
}
|
||
|
||
if transport.wsSettings != nil {
|
||
self.streamWs = transport.wsSettings!
|
||
}
|
||
|
||
if transport.httpSettings != nil {
|
||
self.streamH2 = transport.httpSettings!
|
||
}
|
||
|
||
if transport.dsSettings != nil {
|
||
self.streamDs = transport.dsSettings!
|
||
}
|
||
|
||
if transport.quicSettings != nil {
|
||
self.streamQuic = transport.quicSettings!
|
||
}
|
||
}
|
||
|
||
return (stream)
|
||
}
|
||
|
||
func parseTransport(steamJson: JSON) -> V2rayTransport {
|
||
var stream = V2rayTransport()
|
||
// tlsSettings
|
||
if steamJson["tlsSettings"].dictionaryValue.count > 0 {
|
||
var tlsSettings = TlsSettings()
|
||
tlsSettings.serverName = steamJson["tlsSettings"]["serverName"].stringValue
|
||
tlsSettings.alpn = steamJson["tlsSettings"]["alpn"].stringValue
|
||
tlsSettings.allowInsecure = steamJson["tlsSettings"]["allowInsecure"].boolValue
|
||
tlsSettings.allowInsecureCiphers = steamJson["tlsSettings"]["allowInsecureCiphers"].boolValue
|
||
// certificates
|
||
if steamJson["tlsSettings"]["certificates"].dictionaryValue.count > 0 {
|
||
var certificates = TlsCertificates()
|
||
let usage = TlsCertificates.usage(rawValue: steamJson["tlsSettings"]["certificates"]["usage"].stringValue)
|
||
if (usage != nil) {
|
||
certificates.usage = usage!
|
||
}
|
||
certificates.certificateFile = steamJson["tlsSettings"]["certificates"]["certificateFile"].stringValue
|
||
certificates.keyFile = steamJson["tlsSettings"]["certificates"]["keyFile"].stringValue
|
||
certificates.certificate = steamJson["tlsSettings"]["certificates"]["certificate"].stringValue
|
||
certificates.key = steamJson["tlsSettings"]["certificates"]["key"].stringValue
|
||
tlsSettings.certificates = certificates
|
||
}
|
||
stream.tlsSettings = tlsSettings
|
||
}
|
||
|
||
// tcpSettings
|
||
if steamJson["tcpSettings"].dictionaryValue.count > 0 {
|
||
var tcpSettings = TcpSettings()
|
||
var tcpHeader = TcpSettingHeader()
|
||
|
||
// type
|
||
if steamJson["tcpSettings"]["header"]["type"].stringValue == "http" {
|
||
tcpHeader.type = "http"
|
||
} else {
|
||
tcpHeader.type = "none"
|
||
}
|
||
|
||
// request
|
||
if steamJson["tcpSettings"]["header"]["request"].dictionaryValue.count > 0 {
|
||
var requestJson = steamJson["tcpSettings"]["header"]["request"]
|
||
var tcpRequest = TcpSettingHeaderRequest()
|
||
tcpRequest.version = requestJson["version"].stringValue
|
||
tcpRequest.method = requestJson["method"].stringValue
|
||
tcpRequest.path = requestJson["path"].arrayValue.map {
|
||
$0.stringValue
|
||
}
|
||
|
||
if requestJson["headers"].dictionaryValue.count > 0 {
|
||
var tcpRequestHeaders = TcpSettingHeaderRequestHeaders()
|
||
tcpRequestHeaders.host = requestJson["headers"]["Host"].arrayValue.map {
|
||
$0.stringValue
|
||
}
|
||
tcpRequestHeaders.userAgent = requestJson["headers"]["User-Agent"].arrayValue.map {
|
||
$0.stringValue
|
||
}
|
||
tcpRequestHeaders.acceptEncoding = requestJson["headers"]["Accept-Encoding"].arrayValue.map {
|
||
$0.stringValue
|
||
}
|
||
tcpRequestHeaders.connection = requestJson["headers"]["Connection"].arrayValue.map {
|
||
$0.stringValue
|
||
}
|
||
tcpRequestHeaders.pragma = requestJson["headers"]["Pragma"].stringValue
|
||
tcpRequest.headers = tcpRequestHeaders
|
||
}
|
||
tcpHeader.request = tcpRequest
|
||
}
|
||
|
||
// response
|
||
if steamJson["tcpSettings"]["header"]["response"].dictionaryValue.count > 0 {
|
||
var responseJson = steamJson["tcpSettings"]["header"]["response"]
|
||
var tcpResponse = TcpSettingHeaderResponse()
|
||
|
||
tcpResponse.version = responseJson["version"].stringValue
|
||
tcpResponse.status = responseJson["status"].stringValue
|
||
|
||
if responseJson["headers"].dictionaryValue.count > 0 {
|
||
var tcpResponseHeaders = TcpSettingHeaderResponseHeaders()
|
||
// contentType, transferEncoding, connection
|
||
tcpResponseHeaders.contentType = responseJson["headers"]["Content-Type"].arrayValue.map {
|
||
$0.stringValue
|
||
}
|
||
tcpResponseHeaders.transferEncoding = responseJson["headers"]["Transfer-Encoding"].arrayValue.map {
|
||
$0.stringValue
|
||
}
|
||
tcpResponseHeaders.connection = responseJson["headers"]["Connection"].arrayValue.map {
|
||
$0.stringValue
|
||
}
|
||
tcpResponseHeaders.pragma = responseJson["headers"]["Pragma"].stringValue
|
||
tcpResponse.headers = tcpResponseHeaders
|
||
}
|
||
tcpHeader.response = tcpResponse
|
||
}
|
||
|
||
tcpSettings.header = tcpHeader
|
||
|
||
stream.tcpSettings = tcpSettings
|
||
}
|
||
|
||
// kcpSettings see: https://www.v2ray.com/chapter_02/transport/mkcp.html
|
||
if steamJson["kcpSettings"].dictionaryValue.count > 0 {
|
||
var kcpSettings = KcpSettings()
|
||
kcpSettings.mtu = steamJson["kcpSettings"]["mtu"].intValue
|
||
kcpSettings.tti = steamJson["kcpSettings"]["tti"].intValue
|
||
kcpSettings.uplinkCapacity = steamJson["kcpSettings"]["uplinkCapacity"].intValue
|
||
kcpSettings.downlinkCapacity = steamJson["kcpSettings"]["downlinkCapacity"].intValue
|
||
kcpSettings.congestion = steamJson["kcpSettings"]["congestion"].boolValue
|
||
kcpSettings.readBufferSize = steamJson["kcpSettings"]["readBufferSize"].intValue
|
||
kcpSettings.writeBufferSize = steamJson["kcpSettings"]["writeBufferSize"].intValue
|
||
// "none"
|
||
if KcpSettingsHeaderType.firstIndex(of: steamJson["kcpSettings"]["header"]["type"].stringValue) != nil {
|
||
kcpSettings.header.type = steamJson["kcpSettings"]["header"]["type"].stringValue
|
||
}
|
||
stream.kcpSettings = kcpSettings
|
||
}
|
||
|
||
// wsSettings see: https://www.v2ray.com/chapter_02/transport/websocket.html
|
||
if steamJson["wsSettings"].dictionaryValue.count > 0 {
|
||
var wsSettings = WsSettings()
|
||
wsSettings.path = steamJson["wsSettings"]["path"].stringValue
|
||
wsSettings.headers.host = steamJson["wsSettings"]["headers"]["host"].stringValue
|
||
|
||
stream.wsSettings = wsSettings
|
||
}
|
||
|
||
// (HTTP/2)httpSettings see: https://www.v2ray.com/chapter_02/transport/websocket.html
|
||
if steamJson["httpSettings"].dictionaryValue.count > 0 && steamJson["httpSettings"].dictionaryValue.count > 0 {
|
||
var httpSettings = HttpSettings()
|
||
httpSettings.host = steamJson["httpSettings"]["host"].arrayValue.map {
|
||
$0.stringValue
|
||
}
|
||
httpSettings.path = steamJson["httpSettings"]["path"].stringValue
|
||
|
||
stream.httpSettings = httpSettings
|
||
}
|
||
|
||
// dsSettings
|
||
if steamJson["dsSettings"].dictionaryValue.count > 0 && steamJson["dsSettings"].dictionaryValue.count > 0 {
|
||
var dsSettings = DsSettings()
|
||
dsSettings.path = steamJson["dsSettings"]["path"].stringValue
|
||
stream.dsSettings = dsSettings
|
||
}
|
||
|
||
// quicSettings
|
||
if steamJson["quicSettings"].dictionaryValue.count > 0 && steamJson["quicSettings"].dictionaryValue.count > 0 {
|
||
var quicSettings = QuicSettings()
|
||
quicSettings.key = steamJson["quicSettings"]["key"].stringValue
|
||
// "none"
|
||
if QuicSettingsHeaderType.firstIndex(of: steamJson["quicSettings"]["header"]["type"].stringValue) != nil {
|
||
quicSettings.header.type = steamJson["quicSettings"]["header"]["type"].stringValue
|
||
}
|
||
if QuicSettingsSecurity.firstIndex(of: steamJson["quicSettings"]["security"].stringValue) != nil {
|
||
quicSettings.security = steamJson["quicSettings"]["security"].stringValue
|
||
}
|
||
stream.quicSettings = quicSettings
|
||
}
|
||
|
||
return stream
|
||
}
|
||
|
||
// create current v2ray server json file
|
||
static func createJsonFile(item: V2rayItem) {
|
||
var jsonText = item.json
|
||
|
||
// parse old
|
||
let vCfg = V2rayConfig()
|
||
vCfg.parseJson(jsonText: item.json)
|
||
|
||
// combine new default config
|
||
jsonText = vCfg.combineManual()
|
||
_ = V2rayServer.save(v2ray: item, jsonData: jsonText)
|
||
|
||
// path: /Application/V2rayU.app/Contents/Resources/config.json
|
||
guard let jsonFile = V2rayServer.getJsonFile() else {
|
||
NSLog("unable get config file path")
|
||
return
|
||
}
|
||
|
||
do {
|
||
|
||
let jsonFilePath = URL.init(fileURLWithPath: jsonFile)
|
||
|
||
// delete before config
|
||
if FileManager.default.fileExists(atPath: jsonFile) {
|
||
try? FileManager.default.removeItem(at: jsonFilePath)
|
||
}
|
||
|
||
try jsonText.write(to: jsonFilePath, atomically: true, encoding: String.Encoding.utf8)
|
||
} catch let error {
|
||
// failed to write file – bad permissions, bad filename, missing permissions, or more likely it can't be converted to the encoding
|
||
NSLog("save json file fail: \(error)")
|
||
}
|
||
}
|
||
}
|