Xcode9 beta 5 net/route.h not found

I have a Swift dynamic framework that contain objective-c files for networking purpose,


inside my objective-c file I have these lines, they don't compile anymore on iOS 11 / Xcode 9 Beta 5


#if TARGET_IPHONE_SIMULATOR

#include <net/route.h>

#endif


Error

'net/route.h' file not found


If I set the iOS Simulator to Generic iOS Device, it compiles


any idea ?


thanks


Alex

Replies

<net/route.h>
has never been present in the iOS device SDK, so it’s presence in the iOS simulator SDK is a bit of an anomaly, and a very misleading one at that. It looks that anomaly was resolved in Xcode 9 (yay!).

What are you using this header for?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

thanks Eskimo, I found this code on GitHub and it works so far for the last 2 years. This is to get the router IP address.


If I comment it out, I got this error now


field has incomplete type 'struct rt_msghdr2


the code is


@interface Route_Info : NSObject
{
    struct sockaddr     m_addrs[RTAX_MAX];
    struct rt_msghdr2   m_rtm;
    int                 m_len;      / length of the sockaddr array */
}


Alex

Routing sockets have never been a supported API on iOS, which is why

<net/route.h>
is not part of the standard iOS SDK. This stuff is getting increasingly locked down as the system (and the SDK) evolves, so it would be a bad idea to continue down this path.

Note You can find more context in this post on the old DevForums.

What are you using the router IP address for?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

ok thanks, but I need to do a reverse DNS lookup on the router IP,


I just send you by email my files that I were using


what do you suggest ?


alex

I need to do a reverse DNS lookup on the router IP

Is this for an app that distribute to normal users via the App Store? Or something enterprise specific?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

no this is for an enterprise app, we do not pass by the app store, we deploy it directly in our stores with AirWatch


tahnks

There’s a bunch of ways you can proceed here:

  • If you want to continue down the “IP address of the router” path, you can do a one hop traceroute and grab the router’s IP address from the response.

  • If the routers at all of your sites have different IP addresses, does that mean you use different networks at all of the sites? If so, you could reverse DNS map the device’s IP address, which is trivial to get (via

    getifaddrs
    ). Or algorithmically derive the router’s address from the network address (most folks put the router at the x.x.x.1 or x.x.x.254 position on the network).
  • Alternatively, and this is much easier from a coding perspective, you could survey the BSSIDs of all the sites and then get the BSSID of the network (using

    CNCopyCurrentNetworkInfo
    ) and run that through your BSSID-to-site mapping.
  • Finally, have you considered using Core Location for this? That’s what it was designed for (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

thanks Eskimo, I will check these options one by one, I am not a very experience low level C programmer, for your first 2 solutions, can I use Swift ?


As for Core Location, this will give me the longitude latitude, that means I would need a table with all my retail stores with their coordinare to compare and determine where I am. Also we are using iPod Touch 6 gen, no GPS. Will it works ?


thanks

I will check these options one by one, I am not a very experience low level C programmer, for your first 2 solutions, can I use Swift ?

Yes, but these are low-level C APIs so you’ll need a good understanding of how Swift interacts with such APIs.

With regards the specific APIs I mentioned:

  • Ping and traceroute are both implemented using BSD Sockets. Calling BSD Sockets from Swift is a challenge. I typically use the socket helper infrastructure that you’ll find in the Socket API Helper section of the UnsafeRawPointer Migration doc.

  • With regards

    getifaddrs
    , I’ve written many different wrappers for this in Swift. Pasted in below in my latest attempt.
  • I posted an example of how to call

    CNCopyCurrentNetworkInfo
    from Swift on this thread.

Also we are using iPod Touch 6 gen, no GPS. Will it works ?

Probably. Most day-to-day location tracking doesn’t fire up the GPS hardware — which is both power hungry and slow — but instead relies on Wi-Fi. Naturally this works fine on Wi-Fi only hardware, like the iPod touch.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
import Foundation

struct Interface {
    var name: String
    var addresses: [Data]
    var type: UInt8             // IFT_ETHER and so on
}

extension ifaddrs : Sequence {
    public func makeIterator() -> UnfoldFirstSequence<ifaddrs> {
        return sequence(first: self, next:{ (x) in x.ifa_next?.pointee })
    }
}

extension Interface {

    static func snapshot() -> [Interface] {
        var addrsToFree: UnsafeMutablePointer<ifaddrs>? = nil
        guard getifaddrs(&addrsToFree) == 0, let addrList = addrsToFree?.pointee else {
            return []
        }
        defer {
            freeifaddrs(addrsToFree)
        }
        var interfacesByName: [String:Interface] = [:]
        for addr in addrList {
            // Need the `as Optional` because `ifa_name` is an implicitly unwrapped optional, 
            // and that always gets unwrapped on access so you can't call `flatMap(_:)` on it.
            // 
            // Need the closure (rather than using `String.init(cString:)` because the 
            // compiler finds the latter ambiguous in this context.
            if let name = (addr.ifa_name as Optional).flatMap({String(cString:$0)}), let sa = addr.ifa_addr {
                if interfacesByName[name] == nil {
                   interfacesByName[name] = Interface(name: name, addresses: [], type: 0) 
                }
                switch Int32(sa.pointee.sa_family) {
                    case AF_INET, AF_INET6:
                        let address = Data(bytes: sa, count: Int(sa.pointee.sa_len))
                        interfacesByName[name]!.addresses.append( address )
                    case AF_LINK:
                        if let ifd = addr.ifa_data {
                            let ifd = ifd.assumingMemoryBound(to: if_data.self)
                            interfacesByName[name]!.type = ifd.pointee.ifi_type
                        }
                    default:
                        break
                }
            }
        }
        return [Interface](
            interfacesByName.values
            .filter({$0.type != 0 && !$0.addresses.isEmpty})
            .sorted(by: {$0.name < $1.name})
        )
    }
}

this swift code looks pretty good, the only thing missing is the IP address as a String and not an array of Data,


I will need to figure it out or I could use objective-c code to find it easily


To get the router IP, I could take the iPod IP address and change the last digit by 1


Then I could do a reverse DNS lookup to grab the host name


that part already works, I did it in objective-C, please have a look at my implementation on my GitHub account

https://github.com/agiguere/ReverseDNSLookup


another good wrapper around getifaddrs that I found on github

https://github.com/svdo/swift-netutils


Alex

the only thing missing is the IP address as a String and not an array of Data

You can use

getnameinfo
for that. There’s an example of that pasted in below.

IMPORTANT Make sure to pass in

NI_NUMERICHOST
and
NI_NUMERICSERV
to guarantee that it won’t hit the network.

To get the router IP, I could take the iPod IP address and change the last digit by 1

That’s fine as long as you are sure that all your networks use that convention.

another good wrapper around

getifaddrs
that I found on github

Sure. Although the fact they use

inet_ntop
in the IPv6 case is a bit weird. In my experience
getnameinfo
handles both IPv4 and IPv6 equally well.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
private static func name(for address: Data) -> String {
    return address.withUnsafeBytes { (sa: UnsafePointer<sockaddr>) in
        var nameBytes = [CChar](repeating: 0, count: Int(NI_MAXHOST))
        let err = getnameinfo(
            sa, 
            socklen_t(address.count), 
            &nameBytes, socklen_t(nameBytes.count), 
            nil, 0, 
            NI_NUMERICHOST | NI_NUMERICSERV
        )
        guard err == 0 else {
            return "?"
        }
        return "\(String(cString: nameBytes))"
    }
}

thanks Eskimo, last question


did you had a chance to check my implementation of the reverse DNS on github ?


I merge your code to get the device IP


  static func getDeviceIP() -> String? {
  var addrsToFree: UnsafeMutablePointer<ifaddrs>? = nil
  
  guard getifaddrs(&addrsToFree) == 0, let addrList = addrsToFree?.pointee else {
  return nil
  }
  
  defer {
  freeifaddrs(addrsToFree)
  }
  
  var ipAddress: String?
  
  for addr in addrList {
  // Need the `as Optional` because `ifa_name` is an implicitly unwrapped optional,
  // and that always gets unwrapped on access so you can't call `flatMap(_:)` on it.
  //
  // Need the closure (rather than using `String.init(cString:)` because the
  // compiler finds the latter ambiguous in this context.
  guard let name = (addr.ifa_name as Optional).flatMap({String(cString:$0)}), name == "en0", let sa = addr.ifa_addr else { continue }
  
// let flags = Int32(addr.ifa_flags)
// 
// guard (flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING) else { continue }
  
  guard Int32(sa.pointee.sa_family) == AF_INET else { continue }
  
  var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
  
  if (getnameinfo(&addr.ifa_addr.pointee, socklen_t(addr.ifa_addr.pointee.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
  ipAddress = String(cString: hostname)
  }
  }
  
  return ipAddress
  }

It’s hard to review code like this because you’re writing it for a specific context and, in that context, it makes sense to make simplifying assumptions. For example:

  • Hard-coding

    en0
    is a really bad idea in the general case
  • You’re completely ignoring IPv6 )-:

  • Your

    getDeviceIP()
    API is fundamentally unsound because iOS devices can have multiple IP addresses, some IPv4 and some IPv6, spanning across multiple interfaces

Then again, if these choices make sense for your app that’s cool.

did you had a chance to check my implementation of the reverse DNS on github ?

I took a quick look just now. I have a number of concerns:

  • You’re using a

    imp_implementationWithBlock
    is a neat hack but I’m concerned that it’ll leak. The doc comments for
    imp_implementationWithBlock
    state directly that you have to clean up via
    imp_removeBlock
    , which you don’t do.
  • Putting

    self
    into
    info
    is weird because:
    • You don’t retain the value, which leaves you open to dangling pointers

    • You don’t actually use the

      info
      parameter passed to the block, so you don’t need to do this anyway
  • I don’t like mixing blocks and run loops like you’re doing. Someone calling block-based code like this might be very confused to discover that it relies on the current run loop.

For an example of how I use CFHost for this sort of thing, check out CFHostSample, which I recently updated to include both Swift and Objective-C implementations.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"