punchnet-macos/Tun/NetworkInterface.swift
2026-04-15 17:43:43 +08:00

141 lines
4.6 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// NetworkInterface.swift
// punchnet
//
// Created by on 2025/8/3.
//
//
// NetworkInterface.swift
// Tun
//
// Created by on 2024/1/19.
//
import Foundation
import Network
public struct NetworkInterface {
public let name: String
public let ip: String
public let netmask: String
}
public struct NetworkInterfaceManager {
/**
, (let name: String let ip: String let netmask: String)
*/
public static func getInterfaces() -> [NetworkInterface] {
var interfaces: [NetworkInterface] = []
// Get list of all interfaces on the local machine:
var ifaddr : UnsafeMutablePointer<ifaddrs>? = nil
if getifaddrs(&ifaddr) == 0 {
// For each interface ...
var ptr = ifaddr
while( ptr != nil) {
let flags = Int32(ptr!.pointee.ifa_flags)
var addr = ptr!.pointee.ifa_addr.pointee
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) {
if addr.sa_family == UInt8(AF_INET) || addr.sa_family == UInt8(AF_INET6) {
var mask = ptr!.pointee.ifa_netmask.pointee
// Convert interface address to a human readable string:
let zero = CChar(0)
var hostname = [CChar](repeating: zero, count: Int(NI_MAXHOST))
var netmask = [CChar](repeating: zero, count: Int(NI_MAXHOST))
if (getnameinfo(&addr, socklen_t(addr.sa_len), &hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let address = String(cString: hostname)
let name = ptr!.pointee.ifa_name!
let ifname = String(cString: name)
if (getnameinfo(&mask, socklen_t(mask.sa_len), &netmask, socklen_t(netmask.count), nil, socklen_t(0), NI_NUMERICHOST) == 0) {
let netmaskIP = String(cString: netmask)
interfaces.append(NetworkInterface(name: ifname, ip: address, netmask: netmaskIP))
}
}
}
}
ptr = ptr!.pointee.ifa_next
}
freeifaddrs(ifaddr)
}
return interfaces
}
// IPv6
public static func getPublicIPv6Address() -> String? {
return self.getPublicIPv6Interface()?.ip
}
// IPv6
public static func getPublicIPv6Interface() -> NetworkInterface? {
let interfaces = self.getInterfaces()
return interfaces.first { interface in
!interface.name.hasPrefix("utun")
&& self.isPublicIPv6(interface.ip)
}
}
/// IPv6 IPv6
public static func isPublicIPv6(_ ipString: String) -> Bool {
let normalizedIp = String(ipString.split(separator: "%", maxSplits: 1, omittingEmptySubsequences: false).first ?? "")
guard let ipv6 = IPv6Address(normalizedIp) else {
return false
}
return self.isPublicIPv6(ipv6.rawValue)
}
/// 16 IPv6 IPv6
public static func isPublicIPv6(_ raw: Data) -> Bool {
guard raw.count == 16 else {
return false
}
let bytes = [UInt8](raw)
// 1. unspecified ::
if bytes.allSatisfy({ $0 == 0 }) {
return false
}
// 2. loopback ::1
if bytes.dropLast().allSatisfy({ $0 == 0 }) && bytes[15] == 1 {
return false
}
// 3. multicast ff00::/8
if bytes[0] == 0xFF {
return false
}
// 4. link-local fe80::/10
// 0xFE 10
if bytes[0] == 0xFE && (bytes[1] & 0xC0) == 0x80 {
return false
}
// 5. ULA fc00::/7
// 7 1111110
if (bytes[0] & 0xFE) == 0xFC {
return false
}
// 6. 2000::/3
// 3 001
return (bytes[0] & 0xE0) == 0x20
}
}