Well, you can't win them all.
I did try some clunky manual resolution of addresses last year and plugged it into `selectServerAtIndexPath` to see if different things happened. Long story short: it made me more confused since it worked partially (as in "yellow dot" in-app), but always only on the
second launch of the app — not the first. Since I realized that I don't even know the significance of that yellow light in the GUI I decided to not continue with the guesswork.
I'll leave it here if anyone wants to have a go:
mDnsTest.swift:
import Foundation
@objc
public class mDNS : NSObject {
class func inet(addr: sockaddr_in) -> String {
let ipv4addr = (0...3).map { UInt8(truncatingIfNeeded: addr.sin_addr.s_addr >> ($0 * 8)) }
let addrString = ipv4addr.map(String.init).joined(separator: ".")
print(addrString)
return addrString
}
class func inet6(addr: sockaddr_in6) -> String {
let ipv6addr = [
addr.sin6_addr.__u6_addr.__u6_addr32.0,
addr.sin6_addr.__u6_addr.__u6_addr32.1,
addr.sin6_addr.__u6_addr.__u6_addr32.2,
addr.sin6_addr.__u6_addr.__u6_addr32.3
]
.flatMap { uint32 in
(0...3).map { UInt8(truncatingIfNeeded: uint32 >> ($0 * 8)) }
}
var byteStrings = ipv6addr.map { String(format: "%02X", $0) }
stride(from: 14, through: 2, by: -2).forEach { byteStrings.insert(":", at: $0) }
var addrString = byteStrings.joined()
print(addrString)
return addrString
}
@objc
class func lookup(_ hostname: String) -> String? {
print("resolving: \(hostname)")
let name = hostname as CFString
let host = CFHostCreateWithName(kCFAllocatorDefault, name).takeUnretainedValue()
let error: UnsafeMutablePointer<CFStreamError>? = nil
let resolutionStatus = CFHostStartInfoResolution(host, CFHostInfoType.addresses, error)
print("Resolution returned: \(resolutionStatus)")
//typealias LayoutA = MemoryLayout<sockaddr>
//typealias Layout4 = MemoryLayout<sockaddr_in>
//typealias Layout6 = MemoryLayout<sockaddr_in6>
//
//assert(LayoutA.size == Layout4.size)
//assert(LayoutA.size == Layout6.size) // nope
//assert(LayoutA.stride == Layout4.stride)
//assert(LayoutA.stride == Layout6.stride) // nope
guard error == nil else { fatalError("\(error!.pointee)") }
let addresses = CFHostGetAddressing(host, nil)?.takeRetainedValue() as? [Data] ?? []
print(addresses)
var addressStrings = [String]()
for data in addresses {
let umbp = UnsafeMutableBufferPointer<sockaddr>.allocate(capacity: 1)
data.copyBytes(to: umbp)
guard let sock = umbp.first else { continue }
print("length: \(sock.sa_len), family: \(sock.sa_family)")
switch(Int32(sock.sa_family)) {
case AF_INET: // IPv4
print("inet")
let umbp = UnsafeMutableBufferPointer<sockaddr_in>.allocate(capacity: 1)
data.copyBytes(to: umbp)
guard let addr = umbp.first else { continue }
addressStrings.append(inet(addr: addr))
case AF_INET6: // IPv6
print("inet6")
let umbp = UnsafeMutableBufferPointer<sockaddr_in6>.allocate(capacity: 1)
data.copyBytes(to: umbp)
guard let addr = umbp.first else { continue }
addressStrings.append(inet6(addr: addr))
default:
print("unhandled family: \(sock.sa_family)")
}
}
let result = addressStrings.sorted(by: { $0.count < $1.count }).first
print("address \(hostname) resolved to \(result)")
return result
}
}
HostManagementeViewController:
- (void)selectServerAtIndexPathNSIndexPath*)indexPath {
NSDictionary *item = AppDelegate.instance.arrayServerList[indexPath.row];
AppDelegate.instance.obj.serverDescription = IsEmpty(item[@"serverDescription"]) ? @"" : item[@"serverDescription"];
AppDelegate.instance.obj.serverUser = IsEmpty(item[@"serverUser"]) ? @"" : item[@"serverUser"];
AppDelegate.instance.obj.serverPass = IsEmpty(item[@"serverPass"]) ? @"" : item[@"serverPass"];
BOOL useMdnsTest = YES;
if (!useMdnsTest) {
AppDelegate.instance.obj.serverIP = IsEmpty(item[@"serverIP"]) ? @"" : item[@"serverIP"];
} else {
NSString *resolvedIp = [mDNS lookup: item[@"serverIP"]];
AppDelegate.instance.obj.serverIP = IsEmpty(resolvedIp) ? @"" : [NSString stringWithFormat:@"[%@]", resolvedIp];
}
AppDelegate.instance.obj.serverPort = IsEmpty(item[@"serverPort"]) ? @"" : item[@"serverPort"];
AppDelegate.instance.obj.serverHWAddr = IsEmpty(item[@"serverMacAddress"]) ? @"" : item[@"serverMacAddress"];
AppDelegate.instance.obj.tcpPort = [item[@"tcpPort"] intValue];
}