mirror of
https://github.com/yanue/V2rayU.git
synced 2025-06-27 05:30:09 +00:00
fix #420, #
This commit is contained in:
parent
1593cebf5d
commit
ca9cb03f16
1
Podfile
1
Podfile
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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) {
|
||||
|
@ -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"/>
|
||||
|
@ -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?")
|
||||
}
|
||||
}
|
@ -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")
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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))))
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user