Files
2021-11-17 16:36:19 +03:00

168 lines
4.7 KiB
Swift

//
// InterfaceAddress.swift
// PrivadoVPN
//
// Created by Juraldinio on 8/30/20.
// Copyright © 2020 Privado LLC. All rights reserved.
//
import Foundation
final class InterfaceAddress {
private enum Constants {
static let updateInterval: TimeInterval = 3.0
}
final class Record {
let name: String
private(set) var received: UInt64
private(set) var sent: UInt64
let statistic = Emitter<Record>()
init(name: String, received: UInt64, sent: UInt64) {
self.name = name
self.received = received
self.sent = sent
}
fileprivate func update(received: UInt64, sent: UInt64) {
self.received = received
self.sent = sent
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.statistic.invoke(self)
}
}
}
static var instance = InterfaceAddress()
// MARK: - Properties
private var scheduler: Timer?
private var interfaceRecords = [Record]()
var records: [Record] {
if self.interfaceRecords.isEmpty {
self.interfaceRecords = self.refresh()
}
return self.interfaceRecords
}
func record(with interface: String) -> Record? {
return self.records.first(where: { $0.name == interface })
}
// MARK: - Init
private init() { }
// MARK: -
func resume() {
guard !self.scheduler.isExist else { return }
let scheduler = Timer.scheduledTimer(withTimeInterval: Constants.updateInterval, repeats: true) { [weak self] _ in
self?.update()
}
self.scheduler = scheduler
}
func pause() {
if let scheduler = self.scheduler {
scheduler.invalidate()
scheduler.fire()
}
self.scheduler = nil
}
@discardableResult
func update() -> [Record] {
var new = [Record]()
self.refresh().forEach { rec in
guard let record = self.interfaceRecords.first(where: { $0.name == rec.name }) else {
new.append(rec)
return
}
record.update(received: rec.received, sent: rec.sent)
}
self.interfaceRecords.append(contentsOf: new)
return new
}
func getIpAddresses() -> [String] {
var addresses = [String]()
var ifaddr: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&ifaddr) == 0 else { return addresses }
guard let firstAddr = ifaddr else { return addresses }
for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) {
let interface = ifptr.pointee
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
let address = String(cString: hostname)
addresses.append(address)
}
return addresses
}
// MARK: - Private
private func refresh() -> [Record] {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>?
guard getifaddrs(&interfaceAddresses) == 0 else { return [] }
var pointer = interfaceAddresses
var records = [Record]()
while pointer.isExist {
guard let current = pointer else {
pointer = nil
continue
}
guard let record = InterfaceAddress.getInterfaceRecord(from: current) else {
pointer = current.pointee.ifa_next
continue
}
records.append(record)
pointer = current.pointee.ifa_next
}
freeifaddrs(interfaceAddresses)
return records
}
private static func getInterfaceRecord(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> Record? {
let addr = infoPointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
let networkData = unsafeBitCast(infoPointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
let output = UInt64(networkData.pointee.ifi_obytes)
let input = UInt64(networkData.pointee.ifi_ibytes)
let name = String(cString: infoPointer.pointee.ifa_name)
return Record(name: name, received: input, sent: output)
}
}