2
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)
    }
Ankit Thakur
  • 4,739
  • 1
  • 19
  • 35

2 Answers2

2

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
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • I have tried your solution, but still getting empty gateway address. Did you tried these changes in my method. – Ankit Thakur Jun 25 '17 at 12:08
  • @AnkitThakur: I have only tried to answer your question about how to cast the address structure. There may be other problems in your code. I did not try to run your full code (it does not compile anyway, ROUNDUP is not defined). – Martin R Jun 25 '17 at 12:14
  • Ahh! I have accepted your answer, as it working. RoundUp function is also included. – Ankit Thakur Jun 25 '17 at 12:26
  • But I am still getting WifiGateway address as 0.0.0.0. – Ankit Thakur Jun 25 '17 at 12:27
  • @AnkitThakur: Have you tried to debug your code? Is the condition `if addr_in.sin_addr.s_addr == 0` ever true? What about `if(strcmp(result, char) == 0)` ? – Martin R Jun 25 '17 at 12:35
  • it is always false, seems like bug is in the starting point. Here `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}) ` – Ankit Thakur Jun 25 '17 at 12:46
  • 1
    I have fixed the issue and added them in below answer block as well. – Ankit Thakur Jun 25 '17 at 17:50
  • @AnkitThakur: I have added my version, for the sake of completeness. – Martin R Jun 25 '17 at 18:17
1

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)
    }
Ankit Thakur
  • 4,739
  • 1
  • 19
  • 35