sockets - Cómo lanzar sockadd a sockaddr_in en swift
swift3 casting (2)
Gracias a la respuesta de Martin para este problema, junto con las otras áreas fijadas para obtener la dirección de la puerta de enlace como a continuación:
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)
}
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 que se cuelga en la tercera línea, no se puede inseguroBitCast entre tipos de diferentes tamaños
Aquí está el método completo.
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)
}
withMemoryRebound()
tomar la dirección de addr
(que requiere que addr
sea una variable ), use withMemoryRebound()
para volver a vincularla temporalmente a un puntero sockaddr_in
, que luego se puede desreferenciar:
var addr: sockaddr = ...
let addr_in = withUnsafePointer(to: &addr) {
$0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
$0.pointee
}
}
Hay algunos problemas en su método defaultGatewayAddress()
:
-
sa = sa!.advanced(by: ROUNDUP(a: Int(sa!.pointee.sa_len)))
avanzasa
porsa_len
multiplicado por el tamaño desockaddr
, que no es lo que pretendes. - La prueba
if ((rt!.pointee.rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
debe realizar después del ciclofor i in 0..<RTAX_MAX
. - Con
sa_tab.insert(sa!.pointee, at: Int(i))
inserta nuevos elementos en la matriz en lugar de reemplazarlos.
Tenga en cuenta también que
rt = p!.withMemoryRebound(to: rt_msghdr.self, capacity: 1, {$0})
podría funcionar aquí, pero generalmente no es seguro: el puntero de $0
solo es válido para la ejecución del cierre y no debe pasarse al exterior.
Aquí hay una versión de trabajo de tu código. Es esencialmente una traducción de ¿Cómo puedo determinar la puerta de enlace predeterminada en iPhone? Swift (que a su vez parece estar construido en https://github.com/getlantern/libnatpmp/blob/master/getgateway.c ).
También lo he simplificado un poco y modificado para evitar todos los desempaquetes forzados.
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
}