ios optimize tun proxy

This commit is contained in:
wanghongenpin
2025-05-30 22:01:18 +08:00
parent 111b35f50c
commit 7de9cdf554
21 changed files with 1592 additions and 225 deletions

View File

@@ -21,12 +21,36 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
}
let host = conf["proxyHost"] as! String
let proxyPort = conf["proxyPort"] as! Int
let ipProxy = conf["ipProxy"] as! Bool? ?? false
let proxyPort = conf["proxyPort"] as! Int
let ipProxy = conf["ipProxy"] as! Bool? ?? false
// let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: host)
// let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: host)
NSLog(conf.debugDescription)
networkSettings.mtu = 9000
let ipv4Settings = NEIPv4Settings(addresses: ["10.0.0.2"], subnetMasks: ["255.255.255.255"])
if (ipProxy){
ipv4Settings.includedRoutes = [NEIPv4Route.default()]
ipv4Settings.excludedRoutes = [
NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"),
NEIPv4Route(destinationAddress: "100.64.0.0", subnetMask: "255.192.0.0"),
// NEIPv4Route(destinationAddress: "127.0.0.0", subnetMask: "255.0.0.0"),
NEIPv4Route(destinationAddress: "169.254.0.0", subnetMask: "255.255.0.0"),
NEIPv4Route(destinationAddress: "172.16.0.0", subnetMask: "255.240.0.0"),
NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"),
NEIPv4Route(destinationAddress: "17.0.0.0", subnetMask: "255.0.0.0"),
]
let dns = "223.5.5.5,8.8.8.8"
let dnsSettings = NEDNSSettings(servers: dns.components(separatedBy: ","))
dnsSettings.matchDomains = [""]
dnsSettings.matchDomainsNoSearch = true
networkSettings.dnsSettings = dnsSettings
}
//http
let proxySettings = NEProxySettings()
proxySettings.httpEnabled = true
@@ -34,30 +58,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
proxySettings.httpsEnabled = true
proxySettings.httpsServer = NEProxyServer(address: host, port: proxyPort)
proxySettings.matchDomains = [""]
networkSettings.proxySettings = proxySettings
networkSettings.mtu = 1480
let ipv4Settings = NEIPv4Settings(addresses: ["10.0.0.2"], subnetMasks: ["255.255.255.255"])
if (ipProxy){
ipv4Settings.includedRoutes = [NEIPv4Route.default()]
// ipv4Settings.excludedRoutes = [
// NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"),
// NEIPv4Route(destinationAddress: "100.64.0.0", subnetMask: "255.192.0.0"),
// NEIPv4Route(destinationAddress: "127.0.0.0", subnetMask: "255.0.0.0"),
// NEIPv4Route(destinationAddress: "169.254.0.0", subnetMask: "255.255.0.0"),
// NEIPv4Route(destinationAddress: "172.16.0.0", subnetMask: "255.240.0.0"),
// NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"),
// NEIPv4Route(destinationAddress: "17.0.0.0", subnetMask: "255.0.0.0"),
// ]
let dns = "114.114.114.114,8.8.8.8"
let dnsSettings = NEDNSSettings(servers: dns.components(separatedBy: ","))
dnsSettings.matchDomains = [""]
networkSettings.dnsSettings = dnsSettings
}
networkSettings.proxySettings = proxySettings
networkSettings.ipv4Settings = ipv4Settings

View File

@@ -0,0 +1,8 @@
//
// ProxyPin-Bridging-Header.h
// Runner
//
// Created by wanghongen on 2025/5/28.
//
#import "GBPing.h"

View File

@@ -71,37 +71,34 @@ class Connection{
func closeConnection() {
connectionCloser.closeConnection(connection: self)
}
func addSendData(data: Data) {
self.sendBuffer.append(data)
QueueFactory.instance.getQueue().async(flags: .barrier) {
self.sendBuffer.append(data)
if (self.channel?.state != .ready) {
os_log("Connection %{public}@ is not ready, cannot send data", log: OSLog.default, type: .debug, self.description)
return
}
if (self.nwProtocol == .TCP && self.channel?.state != .ready) {
return
}
self.sendToDestination()
}
self.sendToDestination()
}
//
func sendToDestination() {
QueueFactory.instance.getQueue().async(flags: .barrier) {
os_log("Sending data to destination key %{public}@", log: OSLog.default, type: .debug, self.description)
if (self.sendBuffer.count == 0) {
return
// os_log("Sending data to destination key %{public}@", log: OSLog.default, type: .debug, self.description)
if (self.sendBuffer.count == 0) {
return
}
let data = self.sendBuffer
self.sendBuffer.removeAll()
self.channel?.send(content: data, completion: .contentProcessed({ error in
if let error = error {
os_log("Failed to send data to destination key %{public}@ error: %{public}@", log: OSLog.default, type: .error, self.description, error.localizedDescription)
self.closeConnection()
}
let data = self.sendBuffer
self.sendBuffer.removeAll()
self.channel?.send(content: data, completion: .contentProcessed({ error in
if let error = error {
os_log("Failed to send data to destination key %{public}@ error: %{public}@", log: OSLog.default, type: .error, self.description, error.localizedDescription)
self.closeConnection()
}
}))
}
}))
}
var description: String {

View File

@@ -45,10 +45,13 @@ class ConnectionHandler {
switch ipHeader.protocolNumber {
case ProtocolType.tcp.rawValue:
handleTCPPacket(packet: clientPacketData, ipHeader: ipHeader)
case ProtocolType.udp.rawValue:
handleUDPPacket(clientPacketData: clientPacketData, ipHeader: ipHeader)
break
case ProtocolType.udp.rawValue:
handleUDPPacket(clientPacketData: clientPacketData, ipHeader: ipHeader)
break
case ProtocolType.icmp.rawValue:
handleICMPPacket(clientPacketData: &clientPacketData, ipHeader: ipHeader)
break
default:
os_log("Unsupported IP protocol: %d", log: OSLog.default, type: .error, ipHeader.protocolNumber)
}
@@ -91,13 +94,17 @@ class ConnectionHandler {
}
synchronized(connection) {
// os_log("Received UDP packet", log: OSLog.default, type: .default)
os_log("handle UDP Packet %{public}@", log: OSLog.default, type: .default, connection.description)
if newSession {
ioService.registerSession(connection: connection)
}
let payload = clientPacketData.subdata(in: UDPPacketFactory.UDP_HEADER_LENGTH..<clientPacketData.count)
if ((payload.count + UDPPacketFactory.UDP_HEADER_LENGTH) != udpHeader.length) {
os_log("UDP %{public}@ packet length mismatch: expected %d, got %d", log: OSLog.default, type: .error, connection.description, udpHeader.length, payload.count)
}
// os_log("Received UDP packet", log: OSLog.default, type: .default)
connection.lastIpHeader = ipHeader
connection.lastUdpHeader = udpHeader
manager.addClientData(data: payload, connection: connection)
@@ -116,24 +123,20 @@ class ConnectionHandler {
return
}
// printByteArray(packet)
let dataLength = tcpHeader.payload?.count ?? 0
let sourceIP = ipHeader.sourceIP
let destinationIP = ipHeader.destinationIP
let sourcePort = tcpHeader.sourcePort
let destinationPort = tcpHeader.destinationPort
// os_log("Handling TCP packet for %{public}@ flags:%d", log: OSLog.default, type: .default, Connection.getConnectionKey(nwProtocol: .TCP, destIp: destinationIP, destPort: destinationPort, sourceIp: sourceIP, sourcePort: sourcePort), tcpHeader.flags)
let key = Connection.getConnectionKey(nwProtocol: .TCP, destIp: destinationIP, destPort: destinationPort, sourceIp: sourceIP, sourcePort: sourcePort)
if (tcpHeader.isSYN()) {
// os_log("Received SYN packet %{public}@ seq:%u", log: OSLog.default, type: .default, Connection.getConnectionKey(nwProtocol: .TCP, destIp: destinationIP, destPort: destinationPort, sourceIp: sourceIP, sourcePort: sourcePort), tcpHeader.sequenceNumber)
os_log("Received SYN packet %{public}@ seq:%u", log: OSLog.default, type: .default, key, tcpHeader.sequenceNumber)
// 3-way handshake + create new session
replySynAck(ipHeader: ipHeader, tcpHeader: tcpHeader)
} else if (tcpHeader.isACK()) {
let key = Connection.getConnectionKey(nwProtocol: .TCP, destIp: destinationIP, destPort: destinationPort, sourceIp: sourceIP, sourcePort: sourcePort)
// os_log("Received ACK packet for key: %{public}@", log: OSLog.default, type: .debug, key)
// os_log("Received ACK packet for key: %{public}@", log: OSLog.default, type: .debug, key)
guard let connection = manager.getConnectionByKey(key: key) else {
os_log("Ack for unknown session: %{public}@", log: OSLog.default, type: .default, key)
@@ -150,24 +153,19 @@ class ConnectionHandler {
connection.lastTcpHeader = tcpHeader
if dataLength > 0 {
// initProxyConnect(packet: packet, destinationIP: destinationIP, destinationPort: destinationPort, connection: connection)
// os_log("Received data packet %{public}@ length:%d seq:%u", log: OSLog.default, type: .default, connection.description, dataLength, tcpHeader.sequenceNumber)
//accumulate data from client
// os_log("Received data packet %{public}@ length:%d seq:%u, ack:%u", log: OSLog.default, type: .default, connection.description, dataLength, tcpHeader.sequenceNumber, tcpHeader.ackNumber)
manager.addClientData(data: tcpHeader.payload!, connection: connection)
//send ack to client only if new data was added
sendAck(ipHeader: ipHeader, tcpHeader: tcpHeader, acceptedDataLength: dataLength, connection: connection)
} else {
// os_log("Received ACK packet %{public}@ seq:%u", log: OSLog.default, type: .default, connection.description, tcpHeader.sequenceNumber)
//an ack from client for previously sent data
acceptAck(tcpHeader: tcpHeader, connection: connection)
if connection.isClosingConnection {
sendFinAck(ipHeader: ipHeader, tcpHeader: tcpHeader, connection: connection)
} else if connection.isAckedToFin && !tcpHeader.isFIN() {
//the last ACK from client after FIN-ACK flag was sent
manager.closeConnection(nwProtocol: .TCP, ip: destinationIP, port: destinationPort, srcIp: sourceIP, srcPort: sourcePort)
}
os_log("Received ACK packet %{public}@ seq:%u, ack:%u", log: OSLog.default, type: .default, connection.description, tcpHeader.sequenceNumber, tcpHeader.ackNumber)
}
acceptAck(tcpHeader: tcpHeader, connection: connection)
if connection.isClosingConnection {
sendFinAck(ipHeader: ipHeader, tcpHeader: tcpHeader, connection: connection)
} else if connection.isAckedToFin && !tcpHeader.isFIN() {
manager.closeConnection(nwProtocol: .TCP, ip: destinationIP, port: destinationPort, srcIp: sourceIP, srcPort: sourcePort)
}
//received the last segment of data from vpn client
@@ -274,7 +272,7 @@ class ConnectionHandler {
let ackData = TCPPacketFactory.createResponseAckData(ipHeader: ipHeader, tcpHeader: tcpHeader, ackToClient: ackNumber)
self.write(data: ackData)
// os_log("Sent ACK packet to client %{public}@:%{public}d ack# %{public}d", log: OSLog.default, type: .debug, PacketUtil.intToIPAddress(ipHeader.destinationIP), tcpHeader.destinationPort, ackNumber)
// os_log("Sent ACK packet to client %{public}@ ack: %u", log: OSLog.default, type: .default, connection.description, ackNumber)
}
}
@@ -325,7 +323,7 @@ class ConnectionHandler {
self.ioService.registerSession(connection: connection)
}
self.write(data: packet.buffer)
// os_log("SYN-ACK packet length:%d sent", log: OSLog.default, type: .default, packet.buffer.count)
// os_log("SYN-ACK %{public}@ packet length:%d sent ack:%u", log: OSLog.default, type: .default, connection.description, packet.buffer.count, tcpTransport.ackNumber)
}
}

View File

@@ -14,11 +14,10 @@ class ConnectionManager : CloseableConnection{
//static let instance = ConnectionManager()
private var table: [String: Connection] = [:]
private let tableQueue = DispatchQueue(label: "ProxyPin.ConnectionManager")
public var proxyAddress: NWEndpoint?
private let defaultPorts: [UInt16] = [80, 8080, 8888, 443]
private let defaultPorts: [UInt16] = [80, 443]
func getConnection(nwProtocol: NWProtocol, ip: UInt32, port: UInt16, srcIp: UInt32, srcPort: UInt16) -> Connection? {
@@ -27,87 +26,65 @@ class ConnectionManager : CloseableConnection{
}
func getConnectionByKey(key: String) -> Connection? {
return tableQueue.sync {
return table[key]
}
return table[key]
}
func createTCPConnection(ip: UInt32, port: UInt16, srcIp: UInt32, srcPort: UInt16) -> Connection {
let key = Connection.getConnectionKey(nwProtocol: .TCP, destIp: ip, destPort: port, sourceIp: srcIp, sourcePort: srcPort)
return tableQueue.sync {
if let existingConnection = table[key] {
return existingConnection
}
let connection = Connection(nwProtocol: .TCP, sourceIp: srcIp, sourcePort: srcPort, destinationIp: ip, destinationPort: port, connectionCloser: self)
let parameters = NWParameters.tcp
parameters.allowLocalEndpointReuse = true
parameters.includePeerToPeer = true
let endpoint: NWEndpoint
if defaultPorts.contains(port) {
endpoint = proxyAddress!
} else {
let ipString = PacketUtil.intToIPAddress(ip)
endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(ipString), port: NWEndpoint.Port(rawValue: port)!)
}
let nwConnection = NWConnection(to: endpoint, using: parameters)
connection.channel = nwConnection
connection.isInitConnect = true
tableQueue.async(flags: .barrier) {
self.table[key] = connection
}
os_log("Created TCP connection %{public}@", log: OSLog.default, type: .default, key)
return connection
if let existingConnection = table[key] {
return existingConnection
}
// nwConnection.stateUpdateHandler = { state in
// switch state {
// case .ready:
// connection.isConnected = true
// os_log("Connected to %{public}@", log: OSLog.default, type: .debug, endpoint.debugDescription)
// case .failed(let error):
// connection.isConnected = false
// os_log("Failed to connect: %{public}@", log: OSLog.default, type: .error, error.localizedDescription)
//
// default:
// break
// }
// }
let connection = Connection(nwProtocol: .TCP, sourceIp: srcIp, sourcePort: srcPort, destinationIp: ip, destinationPort: port, connectionCloser: self)
let ipString = PacketUtil.intToIPAddress(ip)
let endpoint: NWEndpoint
if (defaultPorts.contains(port) && !isPrivateIP(ipString)) {
endpoint = proxyAddress!
} else {
endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host(ipString), port: NWEndpoint.Port(rawValue: port)!)
}
// 使 TCP
let parameters = NWParameters.tcp
let nwConnection = NWConnection(to: endpoint, using: parameters)
connection.channel = nwConnection
connection.isInitConnect = true
self.table[key] = connection
os_log("Created TCP connection %{public}@", log: OSLog.default, type: .default, key)
return connection
}
private func isPrivateIP(_ ip: String) -> Bool {
return ip.hasPrefix("10.") ||
ip.hasPrefix("172.") && (16...31).contains(Int(ip.split(separator: ".")[1]) ?? -1) ||
ip.hasPrefix("192.168.")
}
func createUDPConnection(ip: UInt32, port: UInt16, srcIp: UInt32, srcPort: UInt16) -> Connection {
let key = Connection.getConnectionKey(nwProtocol: .UDP, destIp: ip, destPort: port, sourceIp: srcIp, sourcePort: srcPort)
return tableQueue.sync {
if let existingConnection = table[key] {
return existingConnection
}
let connection = Connection(nwProtocol: .UDP, sourceIp: srcIp, sourcePort: srcPort, destinationIp: ip, destinationPort: port, connectionCloser: self)
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host((PacketUtil.intToIPAddress(ip))), port: NWEndpoint.Port(rawValue: port)!)
let nwConnection = NWConnection(to: endpoint, using: .udp)
connection.channel = nwConnection
os_log("Created UDP connection %{public}@", log: OSLog.default, type: .default, key)
connection.isConnected = true
tableQueue.async(flags: .barrier) {
self.table[key] = connection
}
return connection
if let existingConnection = table[key] {
return existingConnection
}
let connection = Connection(nwProtocol: .UDP, sourceIp: srcIp, sourcePort: srcPort, destinationIp: ip, destinationPort: port, connectionCloser: self)
let endpoint = NWEndpoint.hostPort(host: NWEndpoint.Host((PacketUtil.intToIPAddress(ip))), port: NWEndpoint.Port(rawValue: port)!)
let nwConnection = NWConnection(to: endpoint, using: .udp)
connection.channel = nwConnection
os_log("Created UDP connection %{public}@", log: OSLog.default, type: .default, key)
self.table[key] = connection
return connection
}
func closeConnection(connection: Connection) {
@@ -120,14 +97,13 @@ class ConnectionManager : CloseableConnection{
//
func closeConnection(nwProtocol: NWProtocol, ip: UInt32, port: UInt16, srcIp: UInt32, srcPort: UInt16) {
let key = Connection.getConnectionKey(nwProtocol: nwProtocol, destIp: ip, destPort: port, sourceIp: srcIp, sourcePort: srcPort)
tableQueue.async(flags: .barrier) {
if let connection = self.table.removeValue(forKey: key) {
if connection.channel?.state != .cancelled {
connection.channel?.cancel()
os_log("Closed connection %{public}@", log: OSLog.default, type: .debug, key)
} else {
os_log("Connection %{public}@ is already cancelled", log: OSLog.default, type: .debug, key)
}
if let connection = self.table.removeValue(forKey: key) {
if connection.channel?.state != .cancelled {
connection.channel?.cancel()
os_log("Closed connection %{public}@", log: OSLog.default, type: .debug, key)
} else {
os_log("Connection %{public}@ is already cancelled", log: OSLog.default, type: .debug, key)
}
}
}
@@ -140,8 +116,7 @@ class ConnectionManager : CloseableConnection{
connection.addSendData(data: data)
}
//java
func keepSessionAlive(connection: Connection) {
let key = Connection.getConnectionKey(
nwProtocol: connection.nwProtocol,
@@ -150,8 +125,7 @@ class ConnectionManager : CloseableConnection{
sourceIp: connection.sourceIp,
sourcePort: connection.sourcePort
)
tableQueue.async(flags: .barrier) {
self.table[key] = connection
}
self.table[key] = connection
}
}

View File

@@ -11,13 +11,13 @@ import Network
import os.log
class ProxyVpnService {
private let queue: DispatchQueue = DispatchQueue(label: "ProxyPin.ProxyVpnService")
private var packetFlow: NEPacketTunnelFlow
private var connectionHandler: ConnectionHandler
private var socketIOService: SocketIOService
private var isRunning = true;
init(packetFlow: NEPacketTunnelFlow, proxyAddress: Network.NWEndpoint?) {
init(packetFlow: NEPacketTunnelFlow, proxyAddress: Network.NWEndpoint?) {
self.packetFlow = packetFlow
self.socketIOService = SocketIOService(clientPacketWriter: packetFlow)
let manager = ConnectionManager()
@@ -32,20 +32,25 @@ class ProxyVpnService {
A stopped interface should never start again. Create a new interface instead.
*/
func start() {
isRunning = true;
self.readPackets()
}
func stop() {
isRunning = false;
self.socketIOService.stop()
queue.suspend()
}
func readPackets() -> Void {
if (!isRunning) {
return
}
self.packetFlow.readPackets { (packets, protocols) in
// os_log("Read %d packets", packets.count)
for (i, packet) in packets.enumerated() {
self.connectionHandler.handlePacket(packet: packet, version: protocols[i])
self.connectionHandler.handlePacket(packet: packet, version: protocols[i])
}
self.readPackets()
}

View File

@@ -0,0 +1,58 @@
//
// GBPing.h
// GBPing
//
// Created by Luka Mirosevic on 05/11/2012.
// Copyright (c) 2012 Goonbee. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "GBPingSummary.h"
@class GBPingSummary;
@protocol GBPingDelegate;
NS_ASSUME_NONNULL_BEGIN
typedef void(^StartupCallback)(BOOL success, NSError * _Nullable error);
@interface GBPing : NSObject
@property (weak, nonatomic, nullable) id<GBPingDelegate> delegate;
@property (copy, nonatomic, nullable) NSString *host;
@property (assign, atomic) NSTimeInterval pingPeriod;
@property (assign, atomic) NSTimeInterval timeout;
@property (assign, atomic) NSUInteger payloadSize;
@property (assign, atomic) NSUInteger ttl;
@property (assign, atomic) NSUInteger count;
@property (assign, atomic, readonly) BOOL isPinging;
@property (assign, atomic, readonly) BOOL isReady;
@property (assign, atomic) BOOL useIpv4;
@property (assign, atomic) BOOL useIpv6;
@property (assign, atomic) BOOL debug;
-(void)setupWithBlock:(StartupCallback)callback;
-(void)startPinging;
-(void)stop;
@end
@protocol GBPingDelegate <NSObject>
@optional
-(void)ping:(GBPing *)pinger didFinishWithTime:(NSTimeInterval)time;
-(void)ping:(GBPing *)pinger didFailWithError:(NSError *)error;
-(void)ping:(GBPing *)pinger didSendPingWithSummary:(GBPingSummary *)summary;
-(void)ping:(GBPing *)pinger didFailToSendPingWithSummary:(GBPingSummary *)summary error:(NSError *)error;
-(void)ping:(GBPing *)pinger didTimeoutWithSummary:(GBPingSummary *)summary;
-(void)ping:(GBPing *)pinger didReceiveReplyWithSummary:(GBPingSummary *)summary;
-(void)ping:(GBPing *)pinger didReceiveUnexpectedReplyWithSummary:(GBPingSummary *)summary;
@end
NS_ASSUME_NONNULL_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
//
// GBPingHelper.swift
//
import Foundation
public typealias Handler = ((_ response: [String: Any]) -> Void)
public class GBPingHelper: NSObject {
private var ping: GBPing?
private let delegate = PingDelegate()
func start(withHost host: String, ipv4: Bool, ipv6: Bool, count: UInt, interval: TimeInterval, timeout: TimeInterval, ttl: UInt, handler: @escaping Handler) {
ping?.stop()
ping = GBPing()
guard let ping = ping else {
return
}
ping.host = host
ping.useIpv4 = ipv4
ping.useIpv6 = ipv6
ping.count = count
ping.pingPeriod = interval
ping.timeout = timeout
if ttl > 0 {
ping.ttl = ttl
}
delegate.handler = handler
ping.delegate = delegate
ping.setup { success, err in
if let err = err as NSError? {
if err.domain == kCFErrorDomainCFNetwork as String {
handler(["error": "UnknownHost"])
} else {
handler(["error": "UnknownError"])
}
return
}
if success {
self.delegate.transmitted = 0
self.delegate.received = 0
ping.startPinging()
}
}
}
func stop() {
ping?.stop()
}
}
private class PingDelegate: NSObject, GBPingDelegate {
public var handler: Handler?
public var transmitted = 0
public var received = 0
func handle(_ summary: GBPingSummary, error: String? = nil) {
guard let handler = handler else {
return
}
var ret: [String: Any] = [:]
ret["seq"] = summary.sequenceNumber
ret["host"] = summary.host
ret["ip"] = summary.ip
ret["ttl"] = summary.ttl
ret["time"] = summary.rtt
ret["error"] = error
handler(ret)
}
func ping(_ pinger: GBPing, didSendPingWith summary: GBPingSummary) {
transmitted += 1
}
func ping(_ pinger: GBPing, didTimeoutWith summary: GBPingSummary) {
handle(summary, error: "RequestTimedOut")
}
func ping(_ pinger: GBPing, didReceiveReplyWith summary: GBPingSummary) {
received += 1
handle(summary)
}
func ping(_ pinger: GBPing, didFinishWithTime time: TimeInterval) {
guard let handler = handler else {
return
}
var ret: [String: Any] = [:]
ret["time"] = time
ret["received"] = received
ret["transmitted"] = transmitted
handler(ret)
}
}

View File

@@ -0,0 +1,29 @@
//
// GBPingSummary.h
// GBPing
//
// Created by Luka Mirosevic on 05/11/2012.
// Copyright (c) 2012 Goonbee. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface GBPingSummary : NSObject <NSCopying>
typedef enum {
GBPingStatusPending,
GBPingStatusSuccess,
GBPingStatusFail,
} GBPingStatus;
@property (assign, nonatomic) NSUInteger sequenceNumber;
@property (assign, nonatomic) NSUInteger payloadSize;
@property (assign, nonatomic) NSUInteger ttl;
@property (strong, nonatomic, nullable) NSString *host;
@property (strong, nonatomic, nullable) NSString *ip;
@property (strong, nonatomic, nullable) NSDate *sendDate;
@property (strong, nonatomic, nullable) NSDate *receiveDate;
@property (assign, nonatomic) NSTimeInterval rtt;
@property (assign, nonatomic) GBPingStatus status;
@end

View File

@@ -0,0 +1,68 @@
//
// GBPingSummary.m
// GBPing
//
// Created by Luka Mirosevic on 05/11/2012.
// Copyright (c) 2012 Goonbee. All rights reserved.
//
#import "GBPingSummary.h"
@implementation GBPingSummary
#pragma mark - custom acc
-(void)setHost:(NSString *)host {
_host = host;
}
-(NSTimeInterval)rtt {
if (self.sendDate) {
return [self.receiveDate timeIntervalSinceDate:self.sendDate];
}
else {
return 0;
}
}
#pragma mark - copying
-(id)copyWithZone:(NSZone *)zone {
GBPingSummary *copy = [[[self class] allocWithZone:zone] init];
copy.sequenceNumber = self.sequenceNumber;
copy.payloadSize = self.payloadSize;
copy.ttl = self.ttl;
copy.host = [self.host copy];
copy.ip = [self.ip copy];
copy.sendDate = [self.sendDate copy];
copy.receiveDate = [self.receiveDate copy];
copy.status = self.status;
return copy;
}
#pragma mark - memory
-(id)init {
if (self = [super init]) {
self.status = GBPingStatusPending;
}
return self;
}
-(void)dealloc {
self.host = nil;
self.ip = nil;
self.sendDate = nil;
self.receiveDate = nil;
}
#pragma mark - description
-(NSString *)description {
return [NSString stringWithFormat:@"host: %@, ip:%@, seq: %lu, status: %d, ttl: %lu, payloadSize: %lu, sendDate: %@, receiveDate: %@, rtt: %f", self.host, self.ip, (unsigned long)self.sequenceNumber, self.status, (unsigned long)self.ttl, (unsigned long)self.payloadSize, self.sendDate, self.receiveDate, self.rtt];
}
@end

View File

@@ -0,0 +1,80 @@
//
// ICMPHeader.h
// GBPing
//
// Created by Luka Mirosevic on 15/11/2012.
// Copyright (c) 2012 Goonbee. All rights reserved.
//
#ifndef GBPing_ICMPHeader_h
#define GBPing_ICMPHeader_h
#include <AssertMacros.h>
#pragma mark - IP and ICMP On-The-Wire Format
// The following declarations specify the structure of ping packets on the wire.
// IP header structure:
struct IPHeader {
uint8_t versionAndHeaderLength;
uint8_t differentiatedServices;
uint16_t totalLength;
uint16_t identification;
uint16_t flagsAndFragmentOffset;
uint8_t timeToLive;
uint8_t protocol;
uint16_t headerChecksum;
uint8_t sourceAddress[4];
uint8_t destinationAddress[4];
// options...
// data...
};
typedef struct IPHeader IPHeader;
__Check_Compile_Time(sizeof(IPHeader) == 20);
__Check_Compile_Time(offsetof(IPHeader, versionAndHeaderLength) == 0);
__Check_Compile_Time(offsetof(IPHeader, differentiatedServices) == 1);
__Check_Compile_Time(offsetof(IPHeader, totalLength) == 2);
__Check_Compile_Time(offsetof(IPHeader, identification) == 4);
__Check_Compile_Time(offsetof(IPHeader, flagsAndFragmentOffset) == 6);
__Check_Compile_Time(offsetof(IPHeader, timeToLive) == 8);
__Check_Compile_Time(offsetof(IPHeader, protocol) == 9);
__Check_Compile_Time(offsetof(IPHeader, headerChecksum) == 10);
__Check_Compile_Time(offsetof(IPHeader, sourceAddress) == 12);
__Check_Compile_Time(offsetof(IPHeader, destinationAddress) == 16);
// ICMP type and code combinations:
enum {
kICMPv4TypeEchoRequest = 8,
kICMPv4TypeEchoReply = 0
};
enum {
kICMPv6TypeEchoRequest = 128,
kICMPv6TypeEchoReply = 129
};
// ICMP header structure:
struct ICMPHeader {
uint8_t type;
uint8_t code;
uint16_t checksum;
uint16_t identifier;
uint16_t sequenceNumber;
// data...
};
typedef struct ICMPHeader ICMPHeader;
__Check_Compile_Time(sizeof(ICMPHeader) == 8);
__Check_Compile_Time(offsetof(ICMPHeader, type) == 0);
__Check_Compile_Time(offsetof(ICMPHeader, code) == 1);
__Check_Compile_Time(offsetof(ICMPHeader, checksum) == 2);
__Check_Compile_Time(offsetof(ICMPHeader, identifier) == 4);
__Check_Compile_Time(offsetof(ICMPHeader, sequenceNumber) == 6);
#endif

View File

@@ -9,7 +9,6 @@ import NetworkExtension
class ClientPacketWriter: NSObject {
private var packetFlow: NEPacketTunnelFlow
private let packetQueue = DispatchQueue(label: "packetQueue", attributes: .concurrent)
private var isShutdown = false
init(packetFlow: NEPacketTunnelFlow) {
@@ -17,17 +16,11 @@ class ClientPacketWriter: NSObject {
}
func write(data: Data) {
if !self.isShutdown {
packetQueue.async {
self.packetFlow.writePackets([data], withProtocols: [NSNumber(value: AF_INET)])
}
}
self.packetFlow.writePackets([data], withProtocols: [NSNumber(value: AF_INET)])
}
func shutdown() {
packetQueue.async(flags: .barrier) {
self.isShutdown = true
}
self.isShutdown = true
}
}

View File

@@ -28,29 +28,35 @@ class SocketIOService {
queue.async(flags: .barrier) {
self.shutdown = true
}
queue.suspend()
// queue.suspend()
}
//connection client
public func registerSession(connection: Connection) {
connection.channel!.stateUpdateHandler = { state in
// os_log("Connection %{public}@ state changed to %{public}@", log: OSLog.default, type: .default, connection.description, String(describing: state))
switch state {
case .ready:
connection.isConnected = true
os_log("Connected to %{public}@ on receiveMessage", log: OSLog.default, type: .default, connection.description)
//
connection.sendToDestination()
self.receiveMessage(connection: connection)
case .cancelled:
connection.isConnected = false
// os_log("Connection cancelled", log: OSLog.default, type: .default)
os_log("Connection cancelled %{public}@", log: OSLog.default, type: .default, connection.description)
connection.closeConnection()
self.sendFin(connection: connection)
case .failed(let error):
connection.isConnected = false
os_log("Failed to connect: %{public}@", log: OSLog.default, type: .error, error.localizedDescription)
os_log("Failed to connect: %{public}@ %{public}@", log: OSLog.default, type: .error,connection.description, error.localizedDescription)
connection.closeConnection()
self.sendFin(connection: connection)
default:
os_log("Connection %{public}@ entered unhandled state: %{public}@", log: OSLog.default, type: .default, connection.description, String(describing: state))
break
}
}
@@ -84,14 +90,14 @@ class SocketIOService {
return
}
queue.async {
guard let channel = connection.channel else {
os_log("Invalid channel type", log: OSLog.default, type: .error)
return
}
channel.receive(minimumIncompleteLength: 0, maximumLength: Self.maxReceiveBufferSize) { (data, context, isComplete, error) in
// os_log("Received TCP data packet %{public}@ length %d", log: OSLog.default, type: .default, connection.description, data?.count ?? 0)
guard let channel = connection.channel else {
os_log("Invalid channel type", log: OSLog.default, type: .error)
return
}
channel.receive(minimumIncompleteLength: 1, maximumLength: Self.maxReceiveBufferSize) { (data, context, isComplete, error) in
self.queue.async(flags: .barrier) {
os_log("Received TCP data packet %{public}@ length %d", log: OSLog.default, type: .default, connection.description, data?.count ?? 0)
if let error = error {
os_log("Failed to read from TCP socket: %@", log: OSLog.default, type: .error, error as CVarArg)
self.sendFin(connection: connection)
@@ -109,7 +115,6 @@ class SocketIOService {
self.receiveMessage(connection: connection)
if (isComplete) {
self.sendFin(connection: connection)
connection.isAbortingConnection = true
return
}
@@ -158,6 +163,10 @@ class SocketIOService {
}
private func sendFin(connection: Connection) {
if (connection.nwProtocol != .TCP) {
return
}
guard let ipHeader = connection.lastIpHeader, let tcpHeader = connection.lastTcpHeader else {
os_log("Invalid ipHeader or tcpHeader", log: OSLog.default, type: .error)
return
@@ -177,13 +186,14 @@ class SocketIOService {
}
func readUDP(connection: Connection) {
queue.async {
guard let channel = connection.channel else {
os_log("Invalid channel type", log: OSLog.default, type: .error)
return
}
guard let channel = connection.channel else {
os_log("Invalid channel type", log: OSLog.default, type: .error)
return
}
channel.receive(minimumIncompleteLength: 1, maximumLength: 4196) { (data, context, isComplete, error) in
channel.receive(minimumIncompleteLength: 1, maximumLength: 65507) { (data, context, isComplete, error) in
self.queue.async(flags: .barrier) {
if let error = error {
os_log("Failed to read from UDP socket: %@", log: OSLog.default, type: .error, error as CVarArg)
connection.isAbortingConnection = true
@@ -196,13 +206,16 @@ class SocketIOService {
return
}
guard let ipHeader = connection.lastIpHeader, let udpHeader = connection.lastUdpHeader else {
os_log("Missing IP or UDP header for connection %{public}@", log: OSLog.default, type: .error, connection.description)
return
}
let packetData = UDPPacketFactory.createResponsePacket(
ip: connection.lastIpHeader!,
udp: connection.lastUdpHeader!,
ip: ipHeader,
udp: udpHeader,
packetData: data
)
// os_log("Sending UDP data packet to client", log: OSLog.default, type: .default)
self.clientPacketWriter.writePackets([packetData], withProtocols: [NSNumber(value: AF_INET)])
@@ -210,5 +223,5 @@ class SocketIOService {
self.receiveMessage(connection: connection)
}
}
}
}
}

View File

@@ -23,7 +23,6 @@ struct UDPHeader {
}
}
class UDPPacketFactory {
static let UDP_HEADER_LENGTH = 8
@@ -40,15 +39,17 @@ class UDPPacketFactory {
return UDPHeader(sourcePort: srcPort, destinationPort: destPort, length: length, checksum: checksum)
}
//
static func createResponsePacket(ip: IP4Header, udp: UDPHeader, packetData: Data?) -> Data {
var udpLen = 8
if let packetData = packetData {
udpLen += packetData.count
}
let srcPort = udp.destinationPort
let destPort = udp.sourcePort
let ipHeader = ip.copy()
let srcIp = ip.destinationIP
let destIp = ip.sourceIP
@@ -63,15 +64,13 @@ class UDPPacketFactory {
ipHeader.totalLength = UInt16(totalLength)
var ipData = ipHeader.toBytes()
// clear IP checksum
ipData.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in
bytes[10] = 0
bytes[11] = 0
}
//os_log("Create UDP response packet from %{public}@:%{public}d to %{public}@:%{public}d totalLength:%{public}d", log: OSLog.default, type: .default, PacketUtil.intToIPAddress(srcIp), srcPort, PacketUtil.intToIPAddress(destIp), destPort, totalLength)
// calculate checksum for IP header
let ipChecksum = PacketUtil.calculateChecksum(data: ipData, offset: 0, length: ipData.count)
@@ -89,24 +88,16 @@ class UDPPacketFactory {
// copy UDP header to buffer
buffer.append(contentsOf: srcPort.bytes)
buffer.append(contentsOf: destPort.bytes)
buffer.append(contentsOf: UInt16(udpLen).bytes)
let checksum: UInt16 = 0
buffer.append(contentsOf: checksum.bytes)
// UDP
let udpChecksum: UInt16 = 0
buffer.append(contentsOf: udpChecksum.bytes)
if let packetData = packetData {
buffer.append(packetData)
buffer.append(packetData)
}
return buffer
}
//
public static func printPacket(data: Data) {
guard let udpHeader = createUDPHeader(from: data) else {
return
}
os_log("UDP Header: sourcePort: %{public}d, destinationPort: %{public}d, length: %{public}d, checksum: %{public}d", log: OSLog.default, type: .default, udpHeader.sourcePort, udpHeader.destinationPort, udpHeader.length, udpHeader.checksum)
}
}

View File

@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 55;
objects = {
/* Begin PBXBuildFile section */
@@ -25,6 +25,9 @@
9B70772D2A5718FB00F184A9 /* AudioManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B70772C2A5718FB00F184A9 /* AudioManager.swift */; };
9B7077362A5728B900F184A9 /* silence.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 9B7077352A5728B900F184A9 /* silence.mp3 */; };
9B90F5802C183CDE007D7A81 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9B90F5822C183CDE007D7A81 /* InfoPlist.strings */; };
9BAB4FC02DE75CFE0093BFBA /* GBPing.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BAB4FBC2DE75CFE0093BFBA /* GBPing.m */; };
9BAB4FC12DE75CFE0093BFBA /* GBPingSummary.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BAB4FBE2DE75CFE0093BFBA /* GBPingSummary.m */; };
9BAB4FC32DE75D220093BFBA /* GBPingHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BAB4FC22DE75D220093BFBA /* GBPingHelper.swift */; };
9BC4B8CC2B4B48710047DBDD /* PictureInPictureManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BC4B8CB2B4B48710047DBDD /* PictureInPictureManager.swift */; };
9BCA28662C9772DD00C2B46C /* ConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BCA28652C9772DD00C2B46C /* ConnectionHandler.swift */; };
9BCA286A2C97748100C2B46C /* IP4Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BCA28692C97748100C2B46C /* IP4Header.swift */; };
@@ -124,6 +127,13 @@
9B90F57D2C183C7E007D7A81 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/LaunchScreen.strings"; sourceTree = "<group>"; };
9B90F5812C183CDE007D7A81 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
9B90F5832C183CE0007D7A81 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
9BAB4FBB2DE75CFE0093BFBA /* GBPing.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GBPing.h; sourceTree = "<group>"; };
9BAB4FBC2DE75CFE0093BFBA /* GBPing.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GBPing.m; sourceTree = "<group>"; };
9BAB4FBD2DE75CFE0093BFBA /* GBPingSummary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GBPingSummary.h; sourceTree = "<group>"; };
9BAB4FBE2DE75CFE0093BFBA /* GBPingSummary.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GBPingSummary.m; sourceTree = "<group>"; };
9BAB4FBF2DE75CFE0093BFBA /* ICMPHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ICMPHeader.h; sourceTree = "<group>"; };
9BAB4FC22DE75D220093BFBA /* GBPingHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GBPingHelper.swift; sourceTree = "<group>"; };
9BAB4FC42DE75E9A0093BFBA /* ProxyPin-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ProxyPin-Bridging-Header.h"; sourceTree = "<group>"; };
9BC4B8CB2B4B48710047DBDD /* PictureInPictureManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PictureInPictureManager.swift; sourceTree = "<group>"; };
9BCA28652C9772DD00C2B46C /* ConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionHandler.swift; sourceTree = "<group>"; };
9BCA28692C97748100C2B46C /* IP4Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IP4Header.swift; sourceTree = "<group>"; };
@@ -266,10 +276,24 @@
9B0912242A54593A001108B7 /* PacketTunnelProvider.swift */,
9B0912262A54593A001108B7 /* Info.plist */,
9B0912272A54593A001108B7 /* ProxyPin.entitlements */,
9BAB4FC42DE75E9A0093BFBA /* ProxyPin-Bridging-Header.h */,
);
path = ProxyPin;
sourceTree = "<group>";
};
9BAB4FB12DE74F570093BFBA /* ping */ = {
isa = PBXGroup;
children = (
9BAB4FC22DE75D220093BFBA /* GBPingHelper.swift */,
9BAB4FBB2DE75CFE0093BFBA /* GBPing.h */,
9BAB4FBC2DE75CFE0093BFBA /* GBPing.m */,
9BAB4FBD2DE75CFE0093BFBA /* GBPingSummary.h */,
9BAB4FBE2DE75CFE0093BFBA /* GBPingSummary.m */,
9BAB4FBF2DE75CFE0093BFBA /* ICMPHeader.h */,
);
path = ping;
sourceTree = "<group>";
};
9BC4B8D12B4C19ED0047DBDD /* pip */ = {
isa = PBXGroup;
children = (
@@ -283,6 +307,7 @@
9BCA28642C97729000C2B46C /* vpn */ = {
isa = PBXGroup;
children = (
9BAB4FB12DE74F570093BFBA /* ping */,
9BCA287B2C989A8700C2B46C /* socket */,
9BCA28762C98901800C2B46C /* utils */,
9BCA28672C97746200C2B46C /* transport */,
@@ -420,6 +445,7 @@
};
9B09121F2A54593A001108B7 = {
CreatedOnToolsVersion = 14.2;
LastSwiftMigration = 1630;
};
};
};
@@ -484,10 +510,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
@@ -539,10 +569,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
@@ -625,9 +659,12 @@
9BCA288C2C995B3700C2B46C /* UDPHeader.swift in Sources */,
9BCA28732C988E9D00C2B46C /* Packet.swift in Sources */,
9BCA288A2C98C82000C2B46C /* SocketIOService.swift in Sources */,
9BAB4FC02DE75CFE0093BFBA /* GBPing.m in Sources */,
9BAB4FC12DE75CFE0093BFBA /* GBPingSummary.m in Sources */,
9BCA28852C98C6B300C2B46C /* QueueFactory.swift in Sources */,
9BCA286A2C97748100C2B46C /* IP4Header.swift in Sources */,
9BCA287A2C989A7200C2B46C /* Connection.swift in Sources */,
9BAB4FC32DE75D220093BFBA /* GBPingHelper.swift in Sources */,
9BCA28752C988EC400C2B46C /* TransportHeader.swift in Sources */,
9BCA287F2C989AF300C2B46C /* NWProtocol.swift in Sources */,
9BCA286F2C977E4C00C2B46C /* TCPPacketFactory.swift in Sources */,
@@ -971,6 +1008,7 @@
buildSettings = {
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
@@ -998,6 +1036,7 @@
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "ProxyPin/ProxyPin-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -1009,6 +1048,7 @@
buildSettings = {
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
@@ -1034,6 +1074,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "ProxyPin/ProxyPin-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -1044,6 +1085,7 @@
buildSettings = {
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
@@ -1069,6 +1111,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_OBJC_BRIDGING_HEADER = "ProxyPin/ProxyPin-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};

View File

@@ -74,5 +74,7 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSLocalNetworkUsageDescription</key>
<string>Remote Device Connect</string>
</dict>
</plist>

View File

@@ -48,8 +48,13 @@ class VpnManager{
if let manager = manager {
self.observerAdded = true
NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: manager.connection, queue: OperationQueue.main, using: { [unowned self] (notification) -> Void in
self.updateVPNStatus(manager)
})
if (manager.connection.status == .invalid || manager.connection.status == .disconnected){
print("VPN断开: \(String(describing: manager.debugDescription))")
}
})
}
}
}

View File

@@ -252,7 +252,7 @@
"pullConfigFail": "Failed to pull configuration, please check the network connection",
"sync": "Sync",
"invalidQRCode": "Unrecognized QR code",
"remoteConnectFail": "connection failedPlease check if it is allowed on the same LAN and firewall",
"remoteConnectFail": "Connection failedPlease check if it is allowed on the same LAN and firewall",
"remoteConnectSuccessTips": "Your phone needs to enable packet capture in order to capture requests",
"windowMode": "Window Mode",

View File

@@ -105,13 +105,13 @@ class CustomToast extends StatelessWidget {
);
}
void show(BuildContext context) {
void show(BuildContext context, {Alignment alignment = Alignment.bottomLeft}) {
toastification.show(
context: context,
title: Text(message),
icon: icon == null ? null : Icon(icon),
type: type._toastificationType,
alignment: Alignment.bottomLeft,
alignment: alignment,
autoCloseDuration: duration,
style: ToastificationStyle.flat,
pauseOnHover: true,

View File

@@ -28,6 +28,7 @@ import 'package:proxypin/network/components/manager/request_rewrite_manager.dart
import 'package:proxypin/network/components/manager/script_manager.dart';
import 'package:proxypin/network/http/http_client.dart';
import 'package:proxypin/network/util/logger.dart';
import 'package:proxypin/ui/component/app_dialog.dart';
import 'package:proxypin/ui/component/qrcode/qr_scan_view.dart';
import 'package:proxypin/ui/component/utils.dart';
import 'package:proxypin/ui/component/widgets.dart';
@@ -58,7 +59,12 @@ class RemoteModel {
factory RemoteModel.fromJson(Map<String, dynamic> json) {
return RemoteModel(
connect: json['connect'], host: json['host'], port: json['port'], os: json['os'], hostname: json['hostname']);
connect: json['connect'],
host: json['host'],
port: json['port'],
os: json['os'],
hostname: json['hostname'],
ipProxy: json['ipProxy'] == true);
}
RemoteModel copyWith({
@@ -87,7 +93,7 @@ class RemoteModel {
}
Map<String, dynamic> toJson() {
return {'connect': connect, 'host': host, 'port': port, 'os': os, 'hostname': hostname};
return {'connect': connect, 'host': host, 'port': port, 'os': os, 'hostname': hostname, 'ipProxy': ipProxy};
}
}
@@ -390,6 +396,7 @@ class _RemoteDevicePageState extends State<RemoteDevicePage> {
Future<bool> doConnect(String host, int port, {bool? ipProxy}) async {
if (doConnecting) return false;
doConnecting = true;
try {
var response = await HttpClients.get("http://$host:$port/ping", timeout: const Duration(milliseconds: 3000));
if (response.bodyAsString == "pong") {
@@ -424,11 +431,9 @@ class _RemoteDevicePageState extends State<RemoteDevicePage> {
} catch (e) {
logger.e(e);
if (mounted) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(content: Text(localizations.remoteConnectFail));
});
if (mounted) {
CustomToast.error(localizations.remoteConnectFail).show(context, alignment: Alignment.topCenter);
}
}
return false;
} finally {