mirror of
https://github.com/SideStore/SideStore.git
synced 2026-03-27 12:55:40 +01:00
minimuxer: added dynamic lookup of peer for utun(VPN) when not in P2P which is required for sidestore
This commit is contained in:
2
Dependencies/minimuxer
vendored
2
Dependencies/minimuxer
vendored
Submodule Dependencies/minimuxer updated: 40ccd42d1a...8d02a7b62e
@@ -1,132 +0,0 @@
|
|||||||
//
|
|
||||||
// IfManager.swift
|
|
||||||
// AltStore
|
|
||||||
//
|
|
||||||
// Created by ny on 2/27/26.
|
|
||||||
// Copyright © 2026 SideStore. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Network
|
|
||||||
|
|
||||||
fileprivate func uti(_ uint: UInt32) -> String? {
|
|
||||||
var buf = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
||||||
var addr = in_addr(s_addr: uint.bigEndian)
|
|
||||||
guard inet_ntop(AF_INET, &addr, &buf, UInt32(INET_ADDRSTRLEN)) != nil,
|
|
||||||
let str = String(utf8String: buf) else { return nil }
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func socktouint(_ sock: inout sockaddr) -> UInt32 {
|
|
||||||
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 name = String(utf8String: buf) else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
var addr = in_addr()
|
|
||||||
guard name.withCString({ cString in
|
|
||||||
inet_pton(AF_INET, cString, &addr)
|
|
||||||
}) == 1 else { return 0 }
|
|
||||||
return addr.s_addr.bigEndian
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct NetInfo: Hashable, CustomStringConvertible {
|
|
||||||
public let name: String
|
|
||||||
public let hostIP: String
|
|
||||||
public let destIP: String
|
|
||||||
public let maskIP: String
|
|
||||||
|
|
||||||
private let host: UInt32
|
|
||||||
private let dest: UInt32
|
|
||||||
private let mask: UInt32
|
|
||||||
|
|
||||||
init(name: String, host: UInt32, dest: UInt32, mask: UInt32) {
|
|
||||||
self.name = name
|
|
||||||
self.host = host
|
|
||||||
self.dest = dest
|
|
||||||
self.mask = mask
|
|
||||||
self.hostIP = uti(host) ?? "10.7.0.0"
|
|
||||||
self.destIP = uti(dest) ?? "10.7.0.1"
|
|
||||||
self.maskIP = uti(mask) ?? "255.255.255.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
init?(_ ifaddr: ifaddrs) {
|
|
||||||
guard
|
|
||||||
let ianame = String(utf8String: ifaddr.ifa_name)
|
|
||||||
else { return nil }
|
|
||||||
|
|
||||||
let host = socktouint(&ifaddr.ifa_addr.pointee)
|
|
||||||
let dest = socktouint(&ifaddr.ifa_dstaddr.pointee)
|
|
||||||
let mask = socktouint(&ifaddr.ifa_netmask.pointee)
|
|
||||||
|
|
||||||
self.init(name: ianame, host: host, dest: dest, mask: mask)
|
|
||||||
}
|
|
||||||
|
|
||||||
// computed networking values (still numeric internally)
|
|
||||||
public var minIP: UInt32 { host & mask }
|
|
||||||
public var maxIP: UInt32 { host | ~mask }
|
|
||||||
|
|
||||||
public var minIPString: String { uti(minIP) ?? "nil" }
|
|
||||||
public var maxIPString: String { uti(maxIP) ?? "nil" }
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
"\(name) | ip=\(hostIP) dest=\(destIP) mask=\(maskIP) range=\(minIPString)-\(maxIPString)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final class IfManager: Sendable {
|
|
||||||
public static let shared = IfManager()
|
|
||||||
nonisolated(unsafe) private(set) var addrs: Set<NetInfo> = Set()
|
|
||||||
|
|
||||||
private init() {
|
|
||||||
self.addrs = IfManager.query()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public func query() {
|
|
||||||
addrs = IfManager.query()
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func query() -> Set<NetInfo> {
|
|
||||||
var addrs = Set<NetInfo>()
|
|
||||||
var head: UnsafeMutablePointer<ifaddrs>? = nil
|
|
||||||
guard getifaddrs(&head) == 0, let first = head else { return addrs }
|
|
||||||
defer { freeifaddrs(head) }
|
|
||||||
|
|
||||||
var cursor: UnsafeMutablePointer<ifaddrs>? = first
|
|
||||||
while let current = cursor {
|
|
||||||
// we only want v4 interfaces that aren't loopback and aren't masked 255.255.255.255
|
|
||||||
let entry = current.pointee
|
|
||||||
let flags = Int32(entry.ifa_flags)
|
|
||||||
|
|
||||||
let isIPv4 = entry.ifa_addr.pointee.sa_family == UInt8(AF_INET)
|
|
||||||
let isActive = (flags & (IFF_UP | IFF_RUNNING | IFF_LOOPBACK)) == (IFF_UP | IFF_RUNNING)
|
|
||||||
|
|
||||||
if isIPv4, isActive, let info = NetInfo(entry), info.maskIP != "255.255.255.255" {
|
|
||||||
addrs.insert(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor = entry.ifa_next
|
|
||||||
}
|
|
||||||
return addrs
|
|
||||||
}
|
|
||||||
|
|
||||||
private var nextLAN: NetInfo? {
|
|
||||||
addrs.first { $0.name.starts(with: "en") }
|
|
||||||
}
|
|
||||||
|
|
||||||
var nextProbableSideVPN: NetInfo? {
|
|
||||||
// try old 10.7.0.1 first, then fallback to next v4
|
|
||||||
// user should only be connected to StosVPN/LocalDevVPN
|
|
||||||
addrs.first {
|
|
||||||
$0.hostIP == "10.7.0.1" ||
|
|
||||||
$0.name.starts(with: "utun")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var sideVPNPatched: Bool {
|
|
||||||
nextLAN?.maskIP == nextProbableSideVPN?.maskIP &&
|
|
||||||
nextLAN?.maxIP == nextProbableSideVPN?.maxIP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -9,150 +9,147 @@ import Foundation
|
|||||||
import Minimuxer
|
import Minimuxer
|
||||||
|
|
||||||
var isMinimuxerReady: Bool {
|
var isMinimuxerReady: Bool {
|
||||||
|
var result = true
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("isMinimuxerReady = true on simulator")
|
print("[SideStore] isMinimuxerReady = true on simulator")
|
||||||
return true
|
|
||||||
#else
|
#else
|
||||||
IfManager.shared.query()
|
result = Minimuxer.ready()
|
||||||
let dest = IfManager.shared.nextProbableSideVPN?.destIP
|
print("[SideStore] isMinimuxerReady = \(result)")
|
||||||
var result = false
|
#endif
|
||||||
if #available(iOS 26.4, *) {
|
|
||||||
result = Minimuxer.ready(ifaddr: dest) && IfManager.shared.sideVPNPatched
|
|
||||||
} else {
|
|
||||||
result = Minimuxer.ready(ifaddr: dest)
|
|
||||||
}
|
|
||||||
print("isMinimuxerReady = \(result)")
|
|
||||||
return result
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func targetMinimuxerAddress() {
|
||||||
|
defer { print("[SideStore] targetMinimuxerAddress() completed") }
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
print("[SideStore] targetMinimuxerAddress() is no-op on simulator")
|
||||||
|
#else
|
||||||
|
print("[SideStore] targetMinimuxerAddress() invoked")
|
||||||
|
Minimuxer.updateUsbMuxdAddr()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func minimuxerStartWithLogger(_ pairingFile: String, _ logPath: String, _ loggingEnabled: Bool) throws {
|
func minimuxerStartWithLogger(_ pairingFile: String, _ logPath: String, _ loggingEnabled: Bool) throws {
|
||||||
defer { print("minimuxerStartWithLogger(pairingFile, logPath, dest, loggingEnabled) completed") }
|
defer { print("[SideStore] minimuxerStartWithLogger(pairingFile, logPath, dest, loggingEnabled) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("minimuxerStartWithLogger(pairingFile, logPath, loggingEnabled) is no-op on simulator")
|
print("[SideStore] minimuxerStartWithLogger(pairingFile, logPath, loggingEnabled) is no-op on simulator")
|
||||||
#else
|
#else
|
||||||
// IfManager.shared.query()
|
// observe network route changes (and update device endpoint from vpn(utun))
|
||||||
// let dest = IfManager.shared.nextProbableSideVPN?.destIP
|
NetworkObserver.shared.start()
|
||||||
print("minimuxerStartWithLogger(pairingFile, logPath, dest, loggingEnabled) invoked")
|
|
||||||
try Minimuxer.startWithLogger(pairingFile: pairingFile, logPath: logPath, ifaddr: nil, isConsoleLoggingEnabled: loggingEnabled)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
func targetMinimuxerAddress() {
|
print("[SideStore] minimuxerStartWithLogger(pairingFile, logPath, dest, loggingEnabled) invoked")
|
||||||
defer { print("targetMinimuxerAddress() completed") }
|
try Minimuxer.startWithLogger(pairingFile: pairingFile,
|
||||||
#if targetEnvironment(simulator)
|
logPath: logPath,
|
||||||
print("targetMinimuxerAddress() is no-op on simulator")
|
isConsoleLoggingEnabled: loggingEnabled)
|
||||||
#else
|
|
||||||
print("targetMinimuxerAddress() invoked")
|
|
||||||
Minimuxer.targetMinimuxerAddress()
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func installProvisioningProfiles(_ profileData: Data) throws {
|
func installProvisioningProfiles(_ profileData: Data) throws {
|
||||||
defer { print("installProvisioningProfiles(profileData) completed") }
|
defer { print("[SideStore] installProvisioningProfiles(profileData) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("installProvisioningProfiles(profileData) is no-op on simulator")
|
print("[SideStore] installProvisioningProfiles(profileData) is no-op on simulator")
|
||||||
#else
|
#else
|
||||||
print("installProvisioningProfiles(profileData) invoked")
|
print("[SideStore] installProvisioningProfiles(profileData) invoked")
|
||||||
try Minimuxer.installProvisioningProfile(profile: profileData)
|
try Minimuxer.installProvisioningProfile(profile: profileData)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeProvisioningProfile(_ id: String) throws {
|
func removeProvisioningProfile(_ id: String) throws {
|
||||||
defer { print("removeProvisioningProfile(id) completed") }
|
defer { print("[SideStore] removeProvisioningProfile(id) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("removeProvisioningProfile(id) is no-op on simulator")
|
print("[SideStore] removeProvisioningProfile(id) is no-op on simulator")
|
||||||
#else
|
#else
|
||||||
print("removeProvisioningProfile(id) invoked")
|
print("[SideStore] removeProvisioningProfile(id) invoked")
|
||||||
try Minimuxer.removeProvisioningProfile(id: id)
|
try Minimuxer.removeProvisioningProfile(id: id)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeApp(_ bundleId: String) throws {
|
func removeApp(_ bundleId: String) throws {
|
||||||
defer { print("removeApp(bundleId) completed") }
|
defer { print("[SideStore] removeApp(bundleId) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("removeApp(bundleId) is no-op on simulator")
|
print("[SideStore] removeApp(bundleId) is no-op on simulator")
|
||||||
#else
|
#else
|
||||||
print("removeApp(bundleId) invoked")
|
print("[SideStore] removeApp(bundleId) invoked")
|
||||||
try Minimuxer.removeApp(bundleId: bundleId)
|
try Minimuxer.removeApp(bundleId: bundleId)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func yeetAppAFC(_ bundleId: String, _ rawBytes: Data) throws {
|
func yeetAppAFC(_ bundleId: String, _ rawBytes: Data) throws {
|
||||||
defer { print("yeetAppAFC(bundleId, rawBytes) completed") }
|
defer { print("[SideStore] yeetAppAFC(bundleId, rawBytes) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("yeetAppAFC(bundleId, rawBytes) is no-op on simulator")
|
print("[SideStore] yeetAppAFC(bundleId, rawBytes) is no-op on simulator")
|
||||||
#else
|
#else
|
||||||
print("yeetAppAFC(bundleId, rawBytes) invoked")
|
print("[SideStore] yeetAppAFC(bundleId, rawBytes) invoked")
|
||||||
try Minimuxer.yeetAppAfc(bundleId: bundleId, ipaBytes: rawBytes)
|
try Minimuxer.yeetAppAfc(bundleId: bundleId, ipaBytes: rawBytes)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func installIPA(_ bundleId: String) throws {
|
func installIPA(_ bundleId: String) throws {
|
||||||
defer { print("installIPA(bundleId) completed") }
|
defer { print("[SideStore] installIPA(bundleId) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("installIPA(bundleId) is no-op on simulator")
|
print("[SideStore] installIPA(bundleId) is no-op on simulator")
|
||||||
#else
|
#else
|
||||||
print("installIPA(bundleId) invoked")
|
print("[SideStore] installIPA(bundleId) invoked")
|
||||||
try Minimuxer.installIpa(bundleId: bundleId)
|
try Minimuxer.installIpa(bundleId: bundleId)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchUDID() -> String? {
|
func fetchUDID() -> String? {
|
||||||
defer { print("fetchUDID() completed") }
|
defer { print("[SideStore] fetchUDID() completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("fetchUDID() is no-op on simulator")
|
print("[SideStore] fetchUDID() is no-op on simulator")
|
||||||
return "XXXXX-XXXX-XXXXX-XXXX"
|
return "XXXXX-XXXX-XXXXX-XXXX"
|
||||||
#else
|
#else
|
||||||
print("fetchUDID() invoked")
|
print("[SideStore] fetchUDID() invoked")
|
||||||
return Minimuxer.fetchUDID()
|
return Minimuxer.fetchUDID()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func debugApp(_ appId: String) throws {
|
func debugApp(_ appId: String) throws {
|
||||||
defer { print("debugApp(appId) completed") }
|
defer { print("[SideStore] debugApp(appId) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("debugApp(appId) is no-op on simulator")
|
print("[SideStore] debugApp(appId) is no-op on simulator")
|
||||||
#else
|
#else
|
||||||
print("debugApp(appId) invoked")
|
print("[SideStore] debugApp(appId) invoked")
|
||||||
try Minimuxer.debugApp(appId: appId)
|
try Minimuxer.debugApp(appId: appId)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func attachDebugger(_ pid: UInt32) throws {
|
func attachDebugger(_ pid: UInt32) throws {
|
||||||
defer { print("attachDebugger(pid) completed") }
|
defer { print("[SideStore] attachDebugger(pid) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("attachDebugger(pid) is no-op on simulator")
|
print("[SideStore] attachDebugger(pid) is no-op on simulator")
|
||||||
#else
|
#else
|
||||||
print("attachDebugger(pid) invoked")
|
print("[SideStore] attachDebugger(pid) invoked")
|
||||||
try Minimuxer.attachDebugger(pid: pid)
|
try Minimuxer.attachDebugger(pid: pid)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func startAutoMounter(_ docsPath: String) {
|
func startAutoMounter(_ docsPath: String) {
|
||||||
defer { print("startAutoMounter(docsPath) completed") }
|
defer { print("[SideStore] startAutoMounter(docsPath) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("startAutoMounter(docsPath) is no-op on simulator")
|
print("[SideStore] startAutoMounter(docsPath) is no-op on simulator")
|
||||||
#else
|
#else
|
||||||
print("startAutoMounter(docsPath) invoked")
|
print("[SideStore] startAutoMounter(docsPath) invoked")
|
||||||
Minimuxer.startAutoMounter(docsPath: docsPath)
|
Minimuxer.startAutoMounter(docsPath: docsPath)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func dumpProfiles(_ docsPath: String) throws -> String {
|
func dumpProfiles(_ docsPath: String) throws -> String {
|
||||||
defer { print("dumpProfiles(docsPath) completed") }
|
defer { print("[SideStore] dumpProfiles(docsPath) completed") }
|
||||||
#if targetEnvironment(simulator)
|
#if targetEnvironment(simulator)
|
||||||
print("dumpProfiles(docsPath) is no-op on simulator")
|
print("[SideStore] dumpProfiles(docsPath) is no-op on simulator")
|
||||||
return ""
|
return ""
|
||||||
#else
|
#else
|
||||||
print("dumpProfiles(docsPath) invoked")
|
print("[SideStore] dumpProfiles(docsPath) invoked")
|
||||||
return try Minimuxer.dumpProfiles(docsPath: docsPath)
|
return try Minimuxer.dumpProfiles(docsPath: docsPath)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func setMinimuxerDebug(_ debug: Bool) {
|
func setMinimuxerDebug(_ debug: Bool) {
|
||||||
defer { print("setMinimuxerDebug(debug) completed") }
|
defer { print("[SideStore] setMinimuxerDebug(debug) completed") }
|
||||||
print("setMinimuxerDebug(debug) invoked")
|
print("[SideStore] setMinimuxerDebug(debug) invoked")
|
||||||
Minimuxer.setDebug(debug)
|
Minimuxer.setDebug(debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user