78 lines
3.1 KiB
Swift
78 lines
3.1 KiB
Swift
//
|
||
// File.swift
|
||
// sdlan
|
||
//
|
||
// Created by 安礼成 on 2025/7/14.
|
||
//
|
||
|
||
import Foundation
|
||
import NIOCore
|
||
|
||
// 网络类型探测器
|
||
@available(macOS 14, *)
|
||
struct SDLNatProber {
|
||
|
||
// 定义nat类型
|
||
enum NatType: UInt8, Encodable {
|
||
case blocked = 0
|
||
case noNat = 1
|
||
case fullCone = 2
|
||
case portRestricted = 3
|
||
case coneRestricted = 4
|
||
case symmetric = 5
|
||
}
|
||
|
||
// 获取当前所处的网络的nat类型
|
||
static func getNatType(udpHole: SDLUDPHole?, config: SDLConfiguration, logger: SDLLogger) async -> NatType {
|
||
guard let udpHole else {
|
||
return .blocked
|
||
}
|
||
|
||
let addressArray = config.stunProbeSocketAddressArray
|
||
// step1: ip1:port1 <---- ip1:port1
|
||
guard let natAddress1 = await getNatAddress(udpHole, remoteAddress: addressArray[0][0], attr: .none) else {
|
||
return .blocked
|
||
}
|
||
|
||
// 网络没有在nat下
|
||
if await natAddress1 == udpHole.localAddress {
|
||
return .noNat
|
||
}
|
||
|
||
// step2: ip2:port2 <---- ip2:port2
|
||
guard let natAddress2 = await getNatAddress(udpHole, remoteAddress: addressArray[1][1], attr: .none) else {
|
||
return .blocked
|
||
}
|
||
|
||
// 如果natAddress2 的IP地址与上次回来的IP是不一样的,它就是对称型NAT; 这次的包也一定能发成功并收到
|
||
// 如果ip地址变了,这说明{dstIp, dstPort, srcIp, srcPort}, 其中有一个变了;则用新的ip地址
|
||
logger.log("[SDLNatProber] nat_address1: \(natAddress1), nat_address2: \(natAddress2)", level: .debug)
|
||
if let ipAddress1 = natAddress1.ipAddress, let ipAddress2 = natAddress2.ipAddress, ipAddress1 != ipAddress2 {
|
||
return .symmetric
|
||
}
|
||
|
||
// step3: ip1:port1 <---- ip2:port2 (ip地址和port都变的情况)
|
||
// 如果能收到的,说明是完全锥形 说明是IP地址限制锥型NAT,如果不能收到说明是端口限制锥型。
|
||
if let natAddress3 = await getNatAddress(udpHole, remoteAddress: addressArray[0][0], attr: .peer) {
|
||
logger.log("[SDLNatProber] nat_address1: \(natAddress1), nat_address2: \(natAddress2), nat_address3: \(natAddress3)", level: .debug)
|
||
return .fullCone
|
||
}
|
||
|
||
// step3: ip1:port1 <---- ip1:port2 (port改变情况)
|
||
// 如果能收到的说明是IP地址限制锥型NAT,如果不能收到说明是端口限制锥型。
|
||
if let natAddress4 = await getNatAddress(udpHole, remoteAddress: addressArray[0][0], attr: .port) {
|
||
logger.log("[SDLNatProber] nat_address1: \(natAddress1), nat_address2: \(natAddress2), nat_address4: \(natAddress4)", level: .debug)
|
||
return .coneRestricted
|
||
} else {
|
||
return .portRestricted
|
||
}
|
||
}
|
||
|
||
private static func getNatAddress(_ udpHole: SDLUDPHole, remoteAddress: SocketAddress, attr: SDLProbeAttr) async -> SocketAddress? {
|
||
let stunProbeReply = try? await udpHole.stunProbe(remoteAddress: remoteAddress, attr: attr, timeout: 5)
|
||
|
||
return stunProbeReply?.socketAddress()
|
||
}
|
||
|
||
}
|