Last active
February 27, 2026 05:27
-
-
Save nythepegasus/fd8ee8d2f0dba03e66f227d6137caab1 to your computer and use it in GitHub Desktop.
Network interface helper in Swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // authored by @nythepegasus | |
| // with help from others | |
| public struct NetInfo: Hashable { | |
| public let name: String | |
| public let host: String | |
| public let dest: String? | |
| public let mask: String | |
| public init(name: String, host: String, dest: String?, mask: String) { | |
| self.name = name | |
| self.host = host | |
| self.dest = dest | |
| self.mask = mask | |
| } | |
| private init?(_ addr: ifaddrs) { | |
| guard var hostaddr = addr.ifa_addr?.pointee, | |
| var destaddr = addr.ifa_dstaddr?.pointee, | |
| var maskaddr = addr.ifa_netmask?.pointee, | |
| let name = String(utf8String: addr.ifa_name), | |
| let host = NetInfo.sockname(&hostaddr), | |
| let mask = NetInfo.sockname(&maskaddr) else { return nil } | |
| self = .init(name: name, host: host, dest: NetInfo.sockname(&destaddr), mask: mask) | |
| } | |
| private static func sockname(_ sock: inout sockaddr) -> String? { | |
| var buf = [CChar](repeating: 0, count: Int(NI_MAXHOST)) | |
| guard getnameinfo(&sock, socklen_t(sock.sa_len), &buf, socklen_t(buf.count), nil, socklen_t(0), NI_NUMERICHOST) == 0, | |
| let str = String(utf8String: buf) else { | |
| return nil | |
| } | |
| return str | |
| } | |
| public static func addrs(_ check: (ifaddrs) -> Bool = { _ in true }) -> Set<NetInfo> { | |
| var addresses = Set<NetInfo>() | |
| var head: UnsafeMutablePointer<ifaddrs>? = nil | |
| guard getifaddrs(&head) == 0, let first = head else { return addresses } | |
| defer { freeifaddrs(head) } | |
| var ifaddr: UnsafeMutablePointer<ifaddrs>? = first | |
| while let next = ifaddr { | |
| if let info = NetInfo(next.pointee), | |
| check(next.pointee) { addresses.insert(info) } | |
| ifaddr = next.pointee.ifa_next | |
| } | |
| return addresses | |
| } | |
| public static func v4addrs() -> Set<NetInfo> { | |
| addrs { ifaddr in | |
| (ifaddr.ifa_addr.pointee.sa_family == UInt8(AF_INET)) && | |
| (Int32(ifaddr.ifa_flags) & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) | |
| } | |
| } | |
| public static func nextOpenV4() -> NetInfo? { | |
| v4addrs().first { $0.mask != "255.255.255.255" && $0.host != "10.7.0.0" } | |
| } | |
| public static func sideVPN() -> NetInfo? { | |
| addrs().first { $0.host == "10.7.0.0" } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment