This commit is contained in:
yanue 2020-03-02 00:14:50 +08:00
parent 1593cebf5d
commit ca9cb03f16
12 changed files with 175 additions and 348 deletions

View File

@ -17,6 +17,7 @@ target 'V2rayU' do
pod "GCDWebServer"
pod 'SwiftSocket', :git => 'https://github.com/odariusgeorge/SwiftSocket.git', :branch => 'patch-1'
pod 'MASShortcut'
pod 'Swifter', '~> 1.4.7'
end

View File

@ -7,6 +7,7 @@ PODS:
- Preferences (0.3.0)
- QRCoder (1.1.0)
- Sparkle (1.21.3)
- Swifter (1.4.7)
- SwiftSocket (2.0.2)
- SwiftyJSON (5.0.0)
@ -17,6 +18,7 @@ DEPENDENCIES:
- Preferences (from `https://github.com/sindresorhus/Preferences.git`)
- QRCoder
- Sparkle
- Swifter (~> 1.4.7)
- SwiftSocket (from `https://github.com/odariusgeorge/SwiftSocket.git`, branch `patch-1`)
- SwiftyJSON
@ -27,6 +29,7 @@ SPEC REPOS:
- MASShortcut
- QRCoder
- Sparkle
- Swifter
- SwiftyJSON
EXTERNAL SOURCES:
@ -51,9 +54,10 @@ SPEC CHECKSUMS:
Preferences: cfd4b0cbc5adfb5781b819ad3a000324d0fc9f48
QRCoder: cbd2bee531cc86d286df7942334cfed94c803ae4
Sparkle: 3f75576db8b0265adef36c43249d747f22d0b708
Swifter: 2327ef5d872c638aebab79646ce494af508b0c8f
SwiftSocket: 2f7a7e26a7489f5f33b69da914ec366368dfcbcc
SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7
PODFILE CHECKSUM: 407a3efc71b95e99861a0c05290182ea384ac364
PODFILE CHECKSUM: 5f8ca0729788c399da041513b75057075032e5e1
COCOAPODS: 1.8.4

View File

@ -48,7 +48,6 @@
6D6DF5F67A917F78FF652D45 /* .gitignore in Resources */ = {isa = PBXBuildFile; fileRef = 6D6DF8F5112A8407EE959998 /* .gitignore */; };
6D6DF6553CF221AEB71913EE /* PreferenceGeneral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DFFB29020127E034AD168 /* PreferenceGeneral.swift */; };
6D6DF6F065067CD879201FF9 /* Scanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF87840CDA04AF0E9D36E /* Scanner.swift */; };
6D6DF70EA6ADC7628931E630 /* HttpServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF1B2E4D0B61B7390F1B1 /* HttpServer.swift */; };
6D6DF807BE591BE396221EF3 /* PreferenceAdvance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF701C75D8D416096D717 /* PreferenceAdvance.swift */; };
6D6DF87497313615AFE3320A /* PreferencePac.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DF3151BBA6E56836AF6DE /* PreferencePac.swift */; };
6D6DF8BFC33F97E9AFCA5A4B /* V2rayConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D6DFD9F5991B38B128888D6 /* V2rayConfig.swift */; };
@ -194,7 +193,6 @@
6D6DF085C3C411989C416D0D /* user-rule.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "user-rule.txt"; sourceTree = "<group>"; };
6D6DF12EA09D2D80788666D0 /* QrcodeWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QrcodeWindow.swift; sourceTree = "<group>"; };
6D6DF181C5D472DBBB71B72A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = V2rayU/Base.lproj/PreferencePac.xib; sourceTree = SOURCE_ROOT; };
6D6DF1B2E4D0B61B7390F1B1 /* HttpServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpServer.swift; sourceTree = "<group>"; };
6D6DF3151BBA6E56836AF6DE /* PreferencePac.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreferencePac.swift; sourceTree = "<group>"; };
6D6DF3A2857899778B719CE1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = V2rayU/Base.lproj/PreferenceAbout.xib; sourceTree = SOURCE_ROOT; };
6D6DF3C1564D0EBC5A99C9C1 /* gfwlist.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = gfwlist.txt; sourceTree = "<group>"; };
@ -341,7 +339,6 @@
667029D121AFB86E0079EF41 /* QrcodeWindow.xib */,
6D6DF12EA09D2D80788666D0 /* QrcodeWindow.swift */,
6D6DFBED570A0D1FDED0BD53 /* preference */,
6D6DF1B2E4D0B61B7390F1B1 /* HttpServer.swift */,
6618372823E9BF1A000F7410 /* ToastWindow.swift */,
6618372C23E9BF73000F7410 /* ToastWindow.xib */,
66107B8822DEDBE4002FFB60 /* Localizable.strings */,
@ -679,7 +676,6 @@
6D6DF6553CF221AEB71913EE /* PreferenceGeneral.swift in Sources */,
66BC2B89228C589E00FBB716 /* V2raySubscribe.swift in Sources */,
6D6DF807BE591BE396221EF3 /* PreferenceAdvance.swift in Sources */,
6D6DF70EA6ADC7628931E630 /* HttpServer.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -8,6 +8,7 @@
import Cocoa
import ServiceManagement
import Swifter
let launcherAppIdentifier = "net.yanue.V2rayU.Launcher"
let appVersion = getAppVersion()
@ -25,6 +26,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// default settings
self.checkDefault()
// auto Clear Logs
if UserDefaults.getBool(forKey: .autoClearLog) {
print("ClearLogs")
V2rayLaunch.ClearLogs()
}
// auto launch
if UserDefaults.getBool(forKey: .autoLaunch) {
// Insert code here to initialize your application
@ -37,12 +44,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
}
// auto Clear Logs
if UserDefaults.getBool(forKey: .autoClearLog) {
print("ClearLogs")
V2rayLaunch.ClearLogs()
}
// check v2ray core
V2rayCore().check()
// generate plist
@ -128,9 +129,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@objc func onWakeNote(note: NSNotification) {
print("onWakeNote")
if UserDefaults.getBool(forKey: .v2rayTurnOn) {
V2rayLaunch.Start()
}
// check v2ray core
V2rayCore().check()
// auto check updates
@ -147,7 +145,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
@objc func onSleepNote(note: NSNotification) {
V2rayLaunch.Stop()
print("onSleepNote")
}
func applicationWillTerminate(_ aNotification: Notification) {

View File

@ -250,7 +250,7 @@
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Aj-RG-gYn">
<rect key="frame" x="119" y="56" width="222" height="17"/>
<rect key="frame" x="51" y="56" width="359" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Label" id="GyI-8o-VH6">
<font key="font" metaFont="label" size="12"/>

View File

@ -1,302 +0,0 @@
//
// httpServer.swift
// HttpServer
//
// Created by Koksharov Alexandr on 01/12/2018.
// Copyright © 2018 Koksharov Alexandr. All rights reserved.
//
import Foundation
private let maxHeaderSize = 1024
enum HttpServerError: Error {
case description(String)
}
public protocol HttpRequest {
func getMethod() -> String
func getPath() -> String
func getVersion() -> String
func getHeader(name: String) -> String?
func getContent() throws -> String
}
public protocol HttpResponse {
func setHeader(name: String, value: String) -> Void
func setContent(body: String) -> Void
func setStatusCode(code: Int) -> Void
}
class HttpServer {
private let ip: String
private let port: UInt16
private let handler: (HttpRequest, HttpResponse) -> Void
// client sock
private var sock: Int32
private let queue = DispatchQueue(label: "HttpServer", qos: .userInitiated, attributes: .concurrent)
private var source: DispatchSourceRead? = nil
init(ip: String, port: UInt16, handler: @escaping (HttpRequest, HttpResponse) -> Void) {
self.ip = ip
self.port = port
self.handler = handler
self.sock = 0
}
func run() {
var addr = sockaddr_in(
sin_len: UInt8(MemoryLayout<sockaddr_in>.size),
sin_family: UInt8(AF_INET),
sin_port: port.bigEndian,
sin_addr: in_addr(s_addr: INADDR_ANY),
sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)
)
let listenSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)
guard listenSock > -1 else {
print("listenSock \(errno.description)")
return
}
var reuseVal: Int32 = 1
setsockopt(listenSock, SOL_SOCKET, SO_REUSEPORT, &reuseVal, socklen_t(MemoryLayout<Int32>.size))
withUnsafePointer(to: &addr) {
let addrPtr = UnsafeRawPointer($0).assumingMemoryBound(to: sockaddr.self)
guard bind(listenSock, addrPtr, socklen_t(MemoryLayout<sockaddr_in>.size)) == 0 else {
print("withUnsafePointer \(errno.description)")
return
}
}
guard listen(listenSock, 3) == 0 else {
print("listen \(errno.description)")
return
}
self.source = DispatchSource.makeReadSource(fileDescriptor: listenSock, queue: self.queue)
if self.source == nil {
print("cant create dispatch source")
return
}
self.source!.setEventHandler {
var clientAddr = sockaddr_storage()
var clientAddrLen: socklen_t = socklen_t(MemoryLayout.size(ofValue: clientAddr))
let clientSock = withUnsafeMutablePointer(to: &clientAddr) {
accept(listenSock, UnsafeMutableRawPointer($0).assumingMemoryBound(to: sockaddr.self), &clientAddrLen)
}
guard clientSock > 0 else {
print("clientSock \(errno.description)")
return
}
var noSigPipe: Int32 = 1
setsockopt(clientSock, SOL_SOCKET, SO_NOSIGPIPE, &noSigPipe, socklen_t(MemoryLayout<Int32>.size))
self.queue.async {
self.handleClient(socket: clientSock)
self.sock = clientSock
close(clientSock)
}
}
self.source!.resume()
}
func stop() {
if self.sock > 0 {
Darwin.shutdown(sock, SHUT_RDWR)
}
self.queue.async {
close(self.sock)
}
}
private func handleClient(socket sock: Int32) {
do {
let reader: HttpReader = try HttpReader(sock)
let writer: HttpWriter = HttpWriter(sock)
self.handler(reader, writer)
try writer.writeResponse()
} catch let e {
print("\(e)")
}
}
}
private class HttpWriter: HttpResponse {
private let socket: Int32
private let version: String = "HTTP/1.0"
private var statusCode: Int = 200
private var header = [String: String]()
private var body: String = ""
init(_ socket: Int32) {
self.socket = socket
}
func setHeader(name: String, value: String) {
self.header[name] = value
}
func setContent(body: String) {
self.body = body
}
func setStatusCode(code: Int) {
self.statusCode = code
}
private func getStatusLine() -> String {
return "\(version) \(statusCode) \(HTTPURLResponse.localizedString(forStatusCode: statusCode))\r\n"
}
private func writeText(text: String) throws {
try text.withCString { buf in
var written = 0
while written < text.lengthOfBytes(using: String.Encoding.utf8) {
let len = write(self.socket, buf + written, text.lengthOfBytes(using: String.Encoding.utf8) - written)
if len == -1 {
throw HttpServerError.description("\(errno.description)")
}
written = written + len
}
}
}
func writeResponse() throws {
try writeText(text: getStatusLine())
try header.forEach { (key, value) in
try writeText(text: "\(key): \(value)\r\n")
}
try writeText(text: "\r\n\(self.body)")
}
}
private class HttpReader: HttpRequest {
private let socket: Int32
private var isReadDone: Bool
private var method: String = "UNDEF"
private var path: String = "UNDEF"
private var version: String = "UNDEF"
private var header = [String: String]()
private var body: String = ""
private var bodyLength: Int = 0
init(_ sock: Int32) throws {
self.socket = sock
self.isReadDone = false
do {
var httpHeader: String = ""
var reqLine = ""
var reqFields: String = ""
repeat {
let new = try readText(maxLen: maxHeaderSize - httpHeader.lengthOfBytes(using: String.Encoding.utf8))
guard new.lengthOfBytes(using: String.Encoding.utf8) > 0 else {
throw HttpServerError.description("http header too short")
}
httpHeader.append(new)
if let headerRange = httpHeader.range(of: "\r\n\r\n") {
guard let reqLineRange = httpHeader.range(of: "\r\n") else {
throw HttpServerError.description("http requestline read error")
}
reqLine = String(httpHeader[..<reqLineRange.lowerBound])
reqFields = String(httpHeader[reqLineRange.upperBound..<headerRange.lowerBound])
self.body = String(httpHeader[headerRange.upperBound...])
} else {
if httpHeader.lengthOfBytes(using: String.Encoding.utf8) >= maxHeaderSize {
throw HttpServerError.description("http header too long")
}
}
} while reqLine == ""
let requestComponents = reqLine.components(separatedBy: " ")
guard requestComponents.count == 3 else {
throw HttpServerError.description("cant parse request line: '\(reqLine)'")
}
self.method = requestComponents[0]
self.path = requestComponents[1]
self.version = requestComponents[2]
reqFields.enumerateLines { (line, done) in
let field = line.split(separator: ":", maxSplits: 1, omittingEmptySubsequences: true)
if field.count == 2 {
self.header[field[0].lowercased()] = field[1].trimmingCharacters(in: .whitespacesAndNewlines)
} else {
print("cant parse header line. '\(field)'")
}
}
if let lenStr = getHeader(name: "content-length"), let len = Int(lenStr) {
self.bodyLength = len
}
} catch let e {
throw e
}
}
func getMethod() -> String {
return self.method
}
func getPath() -> String {
return self.path
}
func getVersion() -> String {
return self.version
}
func getHeader(name: String) -> String? {
return self.header[name.lowercased()]
}
func getContentLength() -> Int {
return self.bodyLength
}
func getContent() throws -> String {
while self.body.lengthOfBytes(using: String.Encoding.utf8) < self.bodyLength {
do {
let new = try readText(maxLen: Int(self.bodyLength) - self.body.lengthOfBytes(using: String.Encoding.utf8))
self.body.append(new)
} catch let e {
throw e
}
}
if self.body.lengthOfBytes(using: String.Encoding.utf8) > self.bodyLength {
let i = self.body.index(self.body.startIndex, offsetBy: self.bodyLength)
self.body = String(self.body[..<i])
}
return self.body
}
private func readText(maxLen: Int) throws -> String {
let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: maxLen, alignment: 1)
let len = read(self.socket, bytesPointer, maxLen)
if (len == 0) {// client closed stream - EOF
throw HttpServerError.description("Connection closed")
}
if len < 0 {
throw HttpServerError.description(errno.description)
}
if let result = String.init(bytesNoCopy: bytesPointer, length: len, encoding: String.Encoding.utf8, freeWhenDone: true) {
return result
}
throw HttpServerError.description("Request interpretation error. Binary data?")
}
}

View File

@ -253,7 +253,12 @@ class MenuController: NSObject, NSMenuDelegate {
// start v2ray core
func startV2rayCore() {
self.setStatusOff()
NSLog("start v2ray-core begin")
if !V2rayLaunch.checkPorts() {
return
}
guard let v2ray = V2rayServer.loadSelectedItem() else {
noticeTip(title: "start v2ray fail", subtitle: "", informativeText: "v2ray config not found")

View File

@ -86,6 +86,16 @@ final class PreferenceAdvanceViewController: NSViewController, PreferencePane {
let dnsServersVal = self.dnsServers.stringValue
let muxConcurrentVal = self.muxConcurrent.intValue
if httpPortVal == sockPortVal || httpPortVal == pacPortVal || sockPortVal == pacPortVal {
self.tips.stringValue = "the ports(http,sock,pac) cannot be the same"
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
// your code here
self.tips.stringValue = ""
}
return
}
// save
UserDefaults.setBool(forKey: .enableUdp, value: enableUdpVal)
UserDefaults.setBool(forKey: .enableMux, value: enableMuxVal)

View File

@ -246,26 +246,26 @@ func checkTcpPortForListen(port: in_port_t) -> (Bool, descr: String) {
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return (false, "SocketCreationFailed, \(descriptionOfLastError())")
return (false, "SocketCreationFailed, \(descrOfPortError())")
}
var addr = sockaddr_in()
let sizeOfSockkAddr = MemoryLayout<sockaddr_in>.size
addr.sin_len = __uint8_t(sizeOfSockkAddr)
let sizeOfSockAddr = MemoryLayout<sockaddr_in>.size
addr.sin_len = __uint8_t(sizeOfSockAddr)
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("0.0.0.0"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeOfSockkAddr))
memcpy(&bind_addr, &addr, Int(sizeOfSockAddr))
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockkAddr)) == -1 {
let details = descriptionOfLastError()
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockAddr)) == -1 {
let details = descrOfPortError()
releaseTcpPort(socket: socketFileDescriptor)
return (false, "\(port), BindFailed, \(details)")
}
if listen(socketFileDescriptor, SOMAXCONN) == -1 {
let details = descriptionOfLastError()
let details = descrOfPortError()
releaseTcpPort(socket: socketFileDescriptor)
return (false, "\(port), ListenFailed, \(details)")
}
@ -278,7 +278,7 @@ func releaseTcpPort(socket: Int32) {
close(socket)
}
func descriptionOfLastError() -> String {
func descrOfPortError() -> String {
return String.init(cString: (UnsafePointer(strerror(errno))))
}
@ -302,3 +302,28 @@ func shell(_ args: String...) -> String {
return output ?? ""
}
func isPortOpen(port: in_port_t) -> Bool {
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return false
}
var addr = sockaddr_in()
let sizeOfSockAddr = MemoryLayout<sockaddr_in>.size
addr.sin_len = __uint8_t(sizeOfSockAddr)
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("0.0.0.0"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeOfSockAddr))
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockAddr)) == -1 {
return false
}
if listen(socketFileDescriptor, SOMAXCONN) == -1 {
return false
}
return true
}

View File

@ -9,7 +9,7 @@
import Cocoa
import SystemConfiguration
import Alamofire
import GCDWebServer
import Swifter
let LAUNCH_AGENT_DIR = "/Library/LaunchAgents/"
let LAUNCH_AGENT_PLIST = "yanue.v2rayu.v2ray-core.plist"
@ -26,7 +26,7 @@ let cmdSh = AppResourcesPath + "/cmd.sh"
let cmdAppleScript = "do shell script \"" + cmdSh + "\" with administrator privileges"
let JsonConfigFilePath = AppResourcesPath + "/config.json"
let webServer = GCDWebServer()
var webServer = HttpServer()
enum RunMode: String {
case global
@ -55,7 +55,6 @@ class V2rayLaunch: NSObject {
"StandardErrorPath": logFilePath,
"ProgramArguments": agentArguments,
"KeepAlive": true,
"RunAtLoad": true,
]
dictAgent.write(toFile: launchAgentPlistFile, atomically: true)
@ -64,6 +63,7 @@ class V2rayLaunch: NSObject {
if fileMgr.fileExists(atPath: launchHttpPlistFile) {
print("launchHttpPlistFile exist", launchHttpPlistFile)
_ = shell(launchPath: "/bin/launchctl", arguments: ["unload", launchHttpPlistFile])
_ = shell(launchPath: "/bin/launchctl", arguments: ["remove", "yanue.v2rayu.http.plist"])
try! fileMgr.removeItem(atPath: launchHttpPlistFile)
}
@ -79,6 +79,8 @@ class V2rayLaunch: NSObject {
self.startHttpServer()
// unload first
_ = shell(launchPath: "/bin/launchctl", arguments: ["remove", "yanue.v2rayu.v2ray-core"])
_ = shell(launchPath: "/bin/launchctl", arguments: ["remove", "yanue.v2rayu.http.plist"])
_ = shell(launchPath: "/bin/launchctl", arguments: ["unload", launchAgentPlistFile])
let task = Process.launchedProcess(launchPath: "/bin/launchctl", arguments: ["load", "-wF", launchAgentPlistFile])
@ -92,6 +94,8 @@ class V2rayLaunch: NSObject {
static func Stop() {
_ = shell(launchPath: "/bin/launchctl", arguments: ["unload", launchHttpPlistFile])
_ = shell(launchPath: "/bin/launchctl", arguments: ["remove", "yanue.v2rayu.v2ray-core"])
_ = shell(launchPath: "/bin/launchctl", arguments: ["remove", "yanue.v2rayu.http.plist"])
// cmd: /bin/launchctl unload /Library/LaunchAgents/yanue.v2rayu.v2ray-core.plist
let task = Process.launchedProcess(launchPath: "/bin/launchctl", arguments: ["unload", launchAgentPlistFile])
@ -101,9 +105,6 @@ class V2rayLaunch: NSObject {
} else {
NSLog("Stop v2ray-core failed.")
}
// stop http server
webServer.stop()
}
static func OpenLogs() {
@ -164,22 +165,89 @@ class V2rayLaunch: NSObject {
// start http server for pac
static func startHttpServer() {
do {
if webServer.isRunning {
try webServer.stop()
}
// stop first
webServer.stop()
_ = GeneratePACFile(rewrite: false)
// then new HttpServer
webServer = HttpServer()
webServer["/:path"] = shareFilesFromDirectory(AppResourcesPath)
webServer["/pac/:path"] = shareFilesFromDirectory(AppResourcesPath + "/pac")
let pacPort = UserDefaults.get(forKey: .localPacPort) ?? "11085"
webServer.addGETHandler(forBasePath: "/", directoryPath: AppResourcesPath, indexFilename: nil, cacheAge: 0, allowRangeRequests: true)
try webServer.start(options: [
"Port": UInt(pacPort) ?? 11085,
"BindToLocalhost": true
]);
let pacPort = UInt16(UserDefaults.get(forKey: .localPacPort) ?? "11085") ?? 11085
try webServer.start(pacPort)
print("webServer.start at:\(pacPort)")
} catch let error {
print("webServer.start:\(error)")
print("webServer.start error:\(error)")
}
}
static func checkPorts() -> Bool {
let localSockPort = UserDefaults.get(forKey: .localSockPort) ?? "1080"
let localHttpPort = UserDefaults.get(forKey: .localHttpPort) ?? "1087"
let localPacPort = UserDefaults.get(forKey: .localPacPort) ?? "11085"
// check same port
if localSockPort == localHttpPort {
makeToast(message: "the ports (sock,http) cannot be the same: " + localHttpPort)
return false
}
if localHttpPort == localPacPort {
makeToast(message: "the ports (http,pac) cannot be the same:" + localPacPort)
return false
}
if localSockPort == localPacPort {
makeToast(message: "the ports (sock,pac) cannot be the same:" + localPacPort)
return false
}
// check port is used
print("UInt16(localSockPort)", UInt16(localSockPort) ?? 0)
// let (res, err) = self.checkTcpPort(port: UInt16(localSockPort) ?? 0)
// if !res {
// makeToast(message: "the sock port (" + localSockPort + ") has being used: (" + err + ")", displayDuration: 3)
// return false
// }
return true
}
static func checkTcpPort(port: in_port_t) -> (Bool, descr: String) {
let socketFileDescriptor = socket(AF_INET, SOCK_STREAM, 0)
if socketFileDescriptor == -1 {
return (false, "SocketCreationFailed, \(V2rayLaunch.descrOfPortError())")
}
var addr = sockaddr_in()
let sizeOfSockAddr = MemoryLayout<sockaddr_in>.size
addr.sin_len = __uint8_t(sizeOfSockAddr)
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = Int(OSHostByteOrder()) == OSLittleEndian ? _OSSwapInt16(port) : port
addr.sin_addr = in_addr(s_addr: inet_addr("127.0.0.1"))
addr.sin_zero = (0, 0, 0, 0, 0, 0, 0, 0)
var bind_addr = sockaddr()
memcpy(&bind_addr, &addr, Int(sizeOfSockAddr))
if Darwin.bind(socketFileDescriptor, &bind_addr, socklen_t(sizeOfSockAddr)) == -1 {
let details = descrOfPortError()
release(socket: socketFileDescriptor)
return (false, "\(port), BindFailed, \(details)")
}
if listen(socketFileDescriptor, SOMAXCONN) == -1 {
let details = descrOfPortError()
release(socket: socketFileDescriptor)
return (false, "\(port), ListenFailed, \(details)")
}
release(socket: socketFileDescriptor)
return (true, "\(port) is free for use")
}
static func release(socket: Int32) {
Darwin.shutdown(socket, SHUT_RDWR)
close(socket)
}
static func descrOfPortError() -> String {
return String.init(cString: (UnsafePointer(strerror(errno))))
}
}

View File

@ -297,6 +297,20 @@ class V2rayServer: NSObject {
return ""
}
static func save(v2ray: V2rayItem, jsonData: String) {
// store
v2ray.json = jsonData
v2ray.store()
// refresh data
for (idx, item) in self.v2rayItemList.enumerated() {
if item.name == v2ray.name {
self.v2rayItemList[idx].json = jsonData
break
}
}
}
// get by name
static func getIndex(name: String) -> Int {
for (idx, item) in self.v2rayItemList.enumerated() {
@ -375,4 +389,4 @@ class V2rayItem: NSObject, NSCoding {
static func remove(name: String) {
UserDefaults.standard.removeObject(forKey: name)
}
}
}

View File

@ -237,7 +237,7 @@ class V2rayConfig: NSObject {
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
@ -1331,7 +1331,15 @@ class V2rayConfig: NSObject {
// create current v2ray server json file
static func createJsonFile(item: V2rayItem) {
let jsonText = item.json
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 {