mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 11:18:37 +00:00
refactor(macos): share tailnet IPv4 detection
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
import Foundation
|
import Foundation
|
||||||
import Observation
|
import Observation
|
||||||
|
import OpenClawDiscovery
|
||||||
import os
|
import os
|
||||||
#if canImport(Darwin)
|
|
||||||
import Darwin
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// Manages Tailscale integration and status checking.
|
/// Manages Tailscale integration and status checking.
|
||||||
@Observable
|
@Observable
|
||||||
@@ -140,7 +138,7 @@ final class TailscaleService {
|
|||||||
self.logger.info("Tailscale API not responding; app likely not running")
|
self.logger.info("Tailscale API not responding; app likely not running")
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tailscaleIP == nil, let fallback = Self.detectTailnetIPv4() {
|
if self.tailscaleIP == nil, let fallback = TailscaleNetwork.detectTailnetIPv4() {
|
||||||
self.tailscaleIP = fallback
|
self.tailscaleIP = fallback
|
||||||
if !self.isRunning {
|
if !self.isRunning {
|
||||||
self.isRunning = true
|
self.isRunning = true
|
||||||
@@ -178,49 +176,7 @@ final class TailscaleService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private nonisolated static func isTailnetIPv4(_ address: String) -> Bool {
|
|
||||||
let parts = address.split(separator: ".")
|
|
||||||
guard parts.count == 4 else { return false }
|
|
||||||
let octets = parts.compactMap { Int($0) }
|
|
||||||
guard octets.count == 4 else { return false }
|
|
||||||
let a = octets[0]
|
|
||||||
let b = octets[1]
|
|
||||||
return a == 100 && b >= 64 && b <= 127
|
|
||||||
}
|
|
||||||
|
|
||||||
private nonisolated static func detectTailnetIPv4() -> String? {
|
|
||||||
var addrList: UnsafeMutablePointer<ifaddrs>?
|
|
||||||
guard getifaddrs(&addrList) == 0, let first = addrList else { return nil }
|
|
||||||
defer { freeifaddrs(addrList) }
|
|
||||||
|
|
||||||
for ptr in sequence(first: first, next: { $0.pointee.ifa_next }) {
|
|
||||||
let flags = Int32(ptr.pointee.ifa_flags)
|
|
||||||
let isUp = (flags & IFF_UP) != 0
|
|
||||||
let isLoopback = (flags & IFF_LOOPBACK) != 0
|
|
||||||
let family = ptr.pointee.ifa_addr.pointee.sa_family
|
|
||||||
if !isUp || isLoopback || family != UInt8(AF_INET) { continue }
|
|
||||||
|
|
||||||
var addr = ptr.pointee.ifa_addr.pointee
|
|
||||||
var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
||||||
let result = getnameinfo(
|
|
||||||
&addr,
|
|
||||||
socklen_t(ptr.pointee.ifa_addr.pointee.sa_len),
|
|
||||||
&buffer,
|
|
||||||
socklen_t(buffer.count),
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
NI_NUMERICHOST)
|
|
||||||
guard result == 0 else { continue }
|
|
||||||
let len = buffer.prefix { $0 != 0 }
|
|
||||||
let bytes = len.map { UInt8(bitPattern: $0) }
|
|
||||||
guard let ip = String(bytes: bytes, encoding: .utf8) else { continue }
|
|
||||||
if Self.isTailnetIPv4(ip) { return ip }
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
nonisolated static func fallbackTailnetIPv4() -> String? {
|
nonisolated static func fallbackTailnetIPv4() -> String? {
|
||||||
self.detectTailnetIPv4()
|
TailscaleNetwork.detectTailnetIPv4()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
47
apps/macos/Sources/OpenClawDiscovery/TailscaleNetwork.swift
Normal file
47
apps/macos/Sources/OpenClawDiscovery/TailscaleNetwork.swift
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import Darwin
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
public enum TailscaleNetwork {
|
||||||
|
public static func isTailnetIPv4(_ address: String) -> Bool {
|
||||||
|
let parts = address.split(separator: ".")
|
||||||
|
guard parts.count == 4 else { return false }
|
||||||
|
let octets = parts.compactMap { Int($0) }
|
||||||
|
guard octets.count == 4 else { return false }
|
||||||
|
let a = octets[0]
|
||||||
|
let b = octets[1]
|
||||||
|
return a == 100 && b >= 64 && b <= 127
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func detectTailnetIPv4() -> String? {
|
||||||
|
var addrList: UnsafeMutablePointer<ifaddrs>?
|
||||||
|
guard getifaddrs(&addrList) == 0, let first = addrList else { return nil }
|
||||||
|
defer { freeifaddrs(addrList) }
|
||||||
|
|
||||||
|
for ptr in sequence(first: first, next: { $0.pointee.ifa_next }) {
|
||||||
|
let flags = Int32(ptr.pointee.ifa_flags)
|
||||||
|
let isUp = (flags & IFF_UP) != 0
|
||||||
|
let isLoopback = (flags & IFF_LOOPBACK) != 0
|
||||||
|
let family = ptr.pointee.ifa_addr.pointee.sa_family
|
||||||
|
if !isUp || isLoopback || family != UInt8(AF_INET) { continue }
|
||||||
|
|
||||||
|
var addr = ptr.pointee.ifa_addr.pointee
|
||||||
|
var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
||||||
|
let result = getnameinfo(
|
||||||
|
&addr,
|
||||||
|
socklen_t(ptr.pointee.ifa_addr.pointee.sa_len),
|
||||||
|
&buffer,
|
||||||
|
socklen_t(buffer.count),
|
||||||
|
nil,
|
||||||
|
0,
|
||||||
|
NI_NUMERICHOST)
|
||||||
|
guard result == 0 else { continue }
|
||||||
|
let len = buffer.prefix { $0 != 0 }
|
||||||
|
let bytes = len.map { UInt8(bitPattern: $0) }
|
||||||
|
guard let ip = String(bytes: bytes, encoding: .utf8) else { continue }
|
||||||
|
if self.isTailnetIPv4(ip) { return ip }
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
import OpenClawDiscovery
|
||||||
import OpenClawKit
|
import OpenClawKit
|
||||||
import OpenClawProtocol
|
import OpenClawProtocol
|
||||||
#if canImport(Darwin)
|
|
||||||
import Darwin
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct ConnectOptions {
|
struct ConnectOptions {
|
||||||
var url: String?
|
var url: String?
|
||||||
@@ -301,7 +299,7 @@ private func resolvedPassword(opts: ConnectOptions, mode: String, config: Gatewa
|
|||||||
|
|
||||||
private func resolveLocalHost(bind: String?) -> String {
|
private func resolveLocalHost(bind: String?) -> String {
|
||||||
let normalized = (bind ?? "").trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
let normalized = (bind ?? "").trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
|
||||||
let tailnetIP = detectTailnetIPv4()
|
let tailnetIP = TailscaleNetwork.detectTailnetIPv4()
|
||||||
switch normalized {
|
switch normalized {
|
||||||
case "tailnet":
|
case "tailnet":
|
||||||
return tailnetIP ?? "127.0.0.1"
|
return tailnetIP ?? "127.0.0.1"
|
||||||
@@ -309,45 +307,3 @@ private func resolveLocalHost(bind: String?) -> String {
|
|||||||
return "127.0.0.1"
|
return "127.0.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func detectTailnetIPv4() -> String? {
|
|
||||||
var addrList: UnsafeMutablePointer<ifaddrs>?
|
|
||||||
guard getifaddrs(&addrList) == 0, let first = addrList else { return nil }
|
|
||||||
defer { freeifaddrs(addrList) }
|
|
||||||
|
|
||||||
for ptr in sequence(first: first, next: { $0.pointee.ifa_next }) {
|
|
||||||
let flags = Int32(ptr.pointee.ifa_flags)
|
|
||||||
let isUp = (flags & IFF_UP) != 0
|
|
||||||
let isLoopback = (flags & IFF_LOOPBACK) != 0
|
|
||||||
let family = ptr.pointee.ifa_addr.pointee.sa_family
|
|
||||||
if !isUp || isLoopback || family != UInt8(AF_INET) { continue }
|
|
||||||
|
|
||||||
var addr = ptr.pointee.ifa_addr.pointee
|
|
||||||
var buffer = [CChar](repeating: 0, count: Int(NI_MAXHOST))
|
|
||||||
let result = getnameinfo(
|
|
||||||
&addr,
|
|
||||||
socklen_t(ptr.pointee.ifa_addr.pointee.sa_len),
|
|
||||||
&buffer,
|
|
||||||
socklen_t(buffer.count),
|
|
||||||
nil,
|
|
||||||
0,
|
|
||||||
NI_NUMERICHOST)
|
|
||||||
guard result == 0 else { continue }
|
|
||||||
let len = buffer.prefix { $0 != 0 }
|
|
||||||
let bytes = len.map { UInt8(bitPattern: $0) }
|
|
||||||
guard let ip = String(bytes: bytes, encoding: .utf8) else { continue }
|
|
||||||
if isTailnetIPv4(ip) { return ip }
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
private func isTailnetIPv4(_ address: String) -> Bool {
|
|
||||||
let parts = address.split(separator: ".")
|
|
||||||
guard parts.count == 4 else { return false }
|
|
||||||
let octets = parts.compactMap { Int($0) }
|
|
||||||
guard octets.count == 4 else { return false }
|
|
||||||
let a = octets[0]
|
|
||||||
let b = octets[1]
|
|
||||||
return a == 100 && b >= 64 && b <= 127
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user