可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
var sa_tab:[sockaddr?] = [sockaddr](repeating: sockaddr(), count: Int(RTAX_MAX)) let addr:sockaddr = sa_tab[Int(RTAX_DST)]! let addr_in:sockaddr_in = unsafeBitCast(addr.self, to: UnsafeMutablePointer<sockaddr_in>.self).pointee
m getting crash in third line, can’t unsafeBitCast between types of different sizes
Here is the complete method.
func ROUNDUP(a:Int) -> Int{ if (a) > 0 { return (1 + (((a) - 1) | (MemoryLayout<CLong>.size - 1))) } else{ return MemoryLayout<CLong>.size } } class func defaultGatewayAddress() -> Int{ var addressIntValue:UInt32 = 0 var mib:[Int32] = [CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY] let l = UnsafeMutablePointer<Int>.allocate(capacity: MemoryLayout<Int>.size) var buf: UnsafeMutablePointer<CChar>? var p: UnsafeMutablePointer<CChar>? var rt: UnsafeMutablePointer<rt_msghdr>? var sa:UnsafeMutablePointer<sockaddr>? var sa_tab:[sockaddr?] = [sockaddr](repeating: sockaddr(), count: Int(RTAX_MAX)) var _:Int var r:Int = -1 if(sysctl(&mib, u_int(mib.count), nil, l, nil, 0) < 0) { return -1; } print(Int8.max) print(Int8.min) if(l.pointee > 0) { buf = UnsafeMutablePointer<CChar>.allocate(capacity: l.pointee) if(sysctl(&mib, u_int(mib.count), buf, l, nil, 0) < 0) { return -1; } p = buf let maxBuf = buf!.advanced(by: l.pointee) while (p! < maxBuf) { rt = p!.withMemoryRebound(to: rt_msghdr.self, capacity: 1, {$0}) sa = rt!.advanced(by: 1).withMemoryRebound(to: sockaddr.self, capacity: 1, {$0}) for i in 0..<RTAX_MAX { if (rt!.pointee.rtm_addrs & (1 << i)) == 1{ print("insert \(sa!.pointee) at \(i)") sa_tab.insert(sa!.pointee, at: Int(i)) sa = sa!.advanced(by: ROUNDUP(a: Int(sa!.pointee.sa_len))) } else{ sa_tab.insert(nil, at: Int(i)) } if ((rt!.pointee.rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY)) && (sa_tab[Int(RTAX_DST)]?.sa_family == sa_family_t(AF_INET)) && (sa_tab[Int(RTAX_GATEWAY)]?.sa_family == sa_family_t(AF_INET)){ var addr:sockaddr = sa_tab[Int(RTAX_DST)]! let addr_in:sockaddr_in = withUnsafePointer(to: &addr) { $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee } } if addr_in.sin_addr.s_addr == 0 { var buffer = [CChar](repeating: CChar(0), count: Int(IFNAMSIZ) + 1) let result = if_indextoname(UInt32((rt?.pointee.rtm_index)!), &buffer) var char = "en0".cString(using: .utf8) #if arch(i386) || arch(x86_64) // This is a Simulator not an idevice char = "en1".cString(using: .utf8) #endif if(strcmp(result, char) == 0){ r = 0 var gatewayAddr:sockaddr = sa_tab[Int(RTAX_GATEWAY)]! let gatewayAddr_in:sockaddr_in = withUnsafePointer(to: &gatewayAddr) { $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee } } addressIntValue = gatewayAddr_in.sin_addr.s_addr } } } } p = p?.advanced(by: Int((rt?.pointee.rtm_msglen)!)) } buf!.deallocate(capacity: l.pointee) let gatewayLongAddress:UInt32 = addressIntValue print("gatewayLongAddress: \(gatewayLongAddress)") let gatewayIPAddress = String(format:"%d.%d.%d.%d", (gatewayLongAddress & 0xFF), ((gatewayLongAddress >> 8) & 0xFF), ((gatewayLongAddress >> 16) & 0xFF), ((gatewayLongAddress >> 24) & 0xFF)) print("gatewayIPAddress: \(gatewayIPAddress)") } return Int(addressIntValue) }
回答1:
You have to take the address of addr
(which requires addr
to be a variable), use withMemoryRebound()
to temporarily rebind it to an sockaddr_in
pointer, which can then be dereferenced:
var addr: sockaddr = ... let addr_in = withUnsafePointer(to: &addr) { $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee } }
There are some problems in your defaultGatewayAddress()
method:
sa = sa!.advanced(by: ROUNDUP(a: Int(sa!.pointee.sa_len)))
advances sa
by sa_len
multiplied with the size of sockaddr
, which is not what you intend. - The test
if ((rt!.pointee.rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
must be done after the for i in 0..<RTAX_MAX
loop. - With
sa_tab.insert(sa!.pointee, at: Int(i))
you insert new elements into the array instead of replacing them.
Note also that
rt = p!.withMemoryRebound(to: rt_msghdr.self, capacity: 1, {$0})
might work here, but it generally unsafe: The $0
pointer is only valid for the execution of the closure and must not be passed to the outside.
Here is a working version of your code. It is essentially a translation of How can I determine the default gateway on iPhone? to Swift (which in turn seems it built on https://github.com/getlantern/libnatpmp/blob/master/getgateway.c).
I have also simplified it a bit and modified to avoid all forced unwraps.
func defaultGatewayAddress() -> in_addr? { var defaultGateway: in_addr? var mib:[Int32] = [CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY] var len = 0 if sysctl(&mib, u_int(mib.count), nil, &len, nil, 0) < 0 { return nil } let buffer = UnsafeMutablePointer<CChar>.allocate(capacity: len) defer { buffer.deallocate(capacity: len) } if sysctl(&mib, u_int(mib.count), buffer, &len, nil, 0) < 0 { return nil } var sa_tab = [UnsafePointer<sockaddr>?](repeating: nil, count: Int(RTAX_MAX)) var ptr = buffer while ptr < buffer + len { ptr.withMemoryRebound(to: rt_msghdr.self, capacity: 1) { rt in var sa = UnsafeMutableRawPointer(rt + 1).assumingMemoryBound(to: sockaddr.self) for i in 0..<RTAX_MAX { if rt.pointee.rtm_addrs & (1 << i) != 0 { sa_tab[Int(i)] = UnsafePointer(sa) sa = (UnsafeMutableRawPointer(sa) + Int(sa.pointee.sa_len)).assumingMemoryBound(to: sockaddr.self) } else { sa_tab[Int(i)] = nil } } if let dst = sa_tab[Int(RTAX_DST)], dst.pointee.sa_family == sa_family_t(AF_INET), let gateway = sa_tab[Int(RTAX_GATEWAY)], gateway.pointee.sa_family == sa_family_t(AF_INET) { dst.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { addr in if addr.pointee.sin_addr.s_addr == 0 { var name = [CChar](repeating: CChar(0), count: Int(IFNAMSIZ) + 1) if_indextoname(UInt32((rt.pointee.rtm_index)), &name) if String(cString: name) == "en0" { defaultGateway = gateway.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee.sin_addr } } } } } ptr += Int(rt.pointee.rtm_msglen) } } return defaultGateway }
回答2:
Thanks to Martin's answer for this issue, along with that fixed other areas for getting the gateway address as below:
class func defaultGatewayAddress() -> Int{ var addressIntValue:UInt32 = 0 var mib:[Int32] = [CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY] let l = UnsafeMutablePointer<Int>.allocate(capacity: MemoryLayout<Int>.size) var buf: UnsafeMutableRawPointer? var p: UnsafeMutableRawPointer? var rt: UnsafeMutablePointer<rt_msghdr>? var sa_tab:[sockaddr?] = [sockaddr](repeating: sockaddr(), count: Int(RTAX_MAX)) let lengthSysctl = Darwin.sysctl(UnsafeMutablePointer<Int32>(mutating: &mib), UInt32(mib.count), nil, l, nil, 0) if(lengthSysctl < 0) { return -1; } if(l.pointee > 0) { buf = malloc(l.pointee) let bufferSysctl = Darwin.sysctl(UnsafeMutablePointer<Int32>(mutating: &mib), UInt32(mib.count), buf, l, nil, 0) if(bufferSysctl < 0) { return -1; } p = buf let maxBuf = buf!.advanced(by: l.pointee) while (p! < maxBuf) { rt = p!.assumingMemoryBound(to: rt_msghdr.self) let destBuf = p!.advanced(by: MemoryLayout<rt_msghdr>.size) let gatewayBuf = p!.advanced(by: MemoryLayout<rt_msghdr>.size+MemoryLayout<sockaddr>.size*1) let netmaskBuf = p!.advanced(by: MemoryLayout<rt_msghdr>.size+MemoryLayout<sockaddr>.size*2) let interfaceNameBuf = p!.advanced(by: MemoryLayout<rt_msghdr>.size+MemoryLayout<sockaddr>.size*4) let interfaceAddrBuf = p!.advanced(by: MemoryLayout<rt_msghdr>.size+MemoryLayout<sockaddr>.size*5) let dest_sa = destBuf.assumingMemoryBound(to: sockaddr.self) let gateway_sa = gatewayBuf.assumingMemoryBound(to: sockaddr.self) let netmask_sa = netmaskBuf.assumingMemoryBound(to: sockaddr.self) let interfaceName_sa = interfaceNameBuf.assumingMemoryBound(to: sockaddr.self) let interfaceAddr_sa = interfaceAddrBuf.assumingMemoryBound(to: sockaddr.self) let sockAddrs:[Int32:UnsafeMutablePointer<sockaddr>] = [RTAX_DST:dest_sa, RTAX_GATEWAY:gateway_sa, RTAX_NETMASK:netmask_sa, RTAX_IFP:interfaceName_sa, RTAX_IFA:interfaceAddr_sa] for (index, pointer) in sockAddrs { let bin = (rt!.pointee.rtm_addrs & (1 << index)) if bin > 0{ sa_tab.insert(pointer.pointee, at: Int(index)) } else{ sa_tab.insert(nil, at: Int(index)) } } print("RTA_DST -> \((sa_tab[Int(RTAX_DST)]?.sa_family == sa_family_t(AF_INET)))") print("RTA_GATEWAY -> \((sa_tab[Int(RTAX_GATEWAY)]?.sa_family == sa_family_t(AF_INET)))") print("RTAX_DST -> \(RTAX_DST)") print("RTAX_GATEWAY -> \(RTAX_GATEWAY)") if ((rt!.pointee.rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY)) && (sa_tab[Int(RTAX_DST)]?.sa_family == sa_family_t(AF_INET)) && (sa_tab[Int(RTAX_GATEWAY)]?.sa_family == sa_family_t(AF_INET)){ var addr:sockaddr = sa_tab[Int(RTAX_DST)]! let addr_in:sockaddr_in = withUnsafePointer(to: &addr) { $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee } } if addr_in.sin_addr.s_addr == 0 { var buffer = [CChar](repeating: CChar(0), count: Int(IFNAMSIZ) + 1) let result = if_indextoname(UInt32((rt?.pointee.rtm_index)!), &buffer) var char = "en0".cString(using: .utf8) #if arch(i386) || arch(x86_64) // This is a Simulator not an idevice char = "en0".cString(using: .utf8) #endif if(strcmp(result, char) == 0){ var gatewayAddr:sockaddr = sa_tab[Int(RTAX_GATEWAY)]! let gatewayAddr_in:sockaddr_in = withUnsafePointer(to: &gatewayAddr) { $0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { $0.pointee } } addressIntValue = gatewayAddr_in.sin_addr.s_addr } } } p = p?.advanced(by: Int((rt?.pointee.rtm_msglen)!)) } // buf!.deallocate let gatewayLongAddress:UInt32 = addressIntValue print("gatewayLongAddress: \(gatewayLongAddress)") let gatewayIPAddress = String(format:"%d.%d.%d.%d", (gatewayLongAddress & 0xFF), ((gatewayLongAddress >> 8) & 0xFF), ((gatewayLongAddress >> 16) & 0xFF), ((gatewayLongAddress >> 24) & 0xFF)) print("gatewayIPAddress: \(gatewayIPAddress)") } return Int(addressIntValue) }