Networking

RSS for tag

Explore the networking protocols and technologies used by the device to connect to Wi-Fi networks, Bluetooth devices, and cellular data services.

Networking Documentation

Post

Replies

Boosts

Views

Activity

New 60s timer when instantiating PacketTunnelProvider
Somewhere between iOS 16 and iOS 16.5, we've been noticing a new timer when starting up our PacketTunnelProvider. When we start the VPN session and if we take longer than 60 seconds to call the completion handler in (void)startTunnelWithOptions:(NSDictionary *)options completionHandler:(void (^)(NSError *))completionHandler We see that our VPN gets shutdown by the OS. 11:19:15.371532-0700 nesessionmanager NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)]: Plugin NEVPNTunnelPlugin(com.netmotionwireless.Mobility[inactive]) initialized with Mach-O UUIDs ( "69923795-443E-3B0D-9D51-1DC84EB26A08" ) 11:19:15.372733-0700 nesessionmanager NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)] in state NESMVPNSessionStateStarting: plugin NEVPNTunnelPlugin(com.netmotionwireless.Mobility[inactive]) started with PID 27315 error (null) 11:19:15.389348-0700 nesessionmanager NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)] in state NESMVPNSessionStateStarting: plugin NEVPNTunnelPlugin(com.netmotionwireless.Mobility[inactive]) attached IPC with endpoint 0xd5a820210 . . . 11:20:15.290251-0700 nesessionmanager NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)]: State timer (60 seconds) fired in state NESMVPNSessionStateStarting 11:20:15.290375-0700 nesessionmanager NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)] in state NESMVPNSessionStateStarting: timed out 11:20:15.293574-0700 nesessionmanager NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)]: Leaving state NESMVPNSessionStateStarting 11:20:15.293813-0700 nesessionmanager NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)]: Entering state NESMVPNSessionStateStopping, timeout 20 seconds 11:20:15.294034-0700 nesessionmanager NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)]: config request: pushing handler [(null)] (null) 11:20:15.294286-0700 nesessionmanager <NESMServer: 0xd5a904120>: Request to uninstall session: NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)] 11:20:15.294426-0700 nesessionmanager NESMVPNSession[Primary Tunnel:test:7A492A00-109B-4DC9-970F-563A7BBC65A6:(null)]: status changed to disconnecting We can see this with the log message State timer (60 seconds) fired in state NESMVPNSessionStateStarting Is there anything we can do to influence the length of this timer or change the state the VPN is in, other than calling the completion handler? Thanks
6
0
1.1k
Jun ’23
mDNSResponder failing Bonjour Conformance Test
I'm using the following: mDNSResponder 1790.80.10 Bonjour Conformance Test (BCT) 1.5.2 Linux 6.1.y kernel I'm testing an Airplay 2 speaker as part of our self certification. When BCT gets into the mDNS tests mDNSResponder fails the subsequent conflict test with this message: ERROR 2023-06-12 10:37:29.398711-0500 _sub_conflict 03570: Device did not complete its probing sequence for a new name after a subsequent conflict arose for its previously acquired name. BCT then retries three times with each retry failing with the same message. Am I missing something from my software that interacts with the mdns daemon? Is this a known issue with the posix build for mDNSResponder? What can I do to get this test to pass? Any help would be appreciated. Ethan
5
0
1.5k
Jun ’23
TCC configuration (endpoint security extension) failing via MDM on Ventura
Hello there. We have an endpoint security service that consists of a command-line tool and a client app that bundles a network extension (the command-line tool runs as a daemon via Launch Services and communicates with the extension via XPC). It works when installed manually under all OS versions, and under MacOS 12.x (Monterey) and earlier when provisioned via MDM. However, beginning with some version of 13.x (Ventura), MDM provisioning is insufficient. The daemon is unable to connect to the extension via XPC. Under "Full Disk Access" in System Pref^H^H^H^HSettings, an entry for our component appears but the switch is off. Turning the switch on manually at this point does not change the situation; the daemon apparently remains unable to talk to the extension. It seems as though some additional entitlement or declaration is now needed in the MDM mobileconfig to make things work under 13.x and above, but after trying a multitude of combinations, I'm at a loss. Any hints?
6
0
1.6k
Jun ’23
NWTXTRecord dictionary keys are lowercased in iOS 17 beta
NWTXTRecord dictionary keys are lowercased in iOS 17, on iOS Simulator and the device. Records returned by NWBrowser in the listener block: browser.browseResultsChangedHandler = { result, changes in metadata : ["dvty": “AppName”, "txtvers": "1", "dbid": "50BFB79F"] But the actual keys are: "DvTy", "DbId". So, in iOS 17 all keys were lowercased, but not in any previous versions. And if in the app we were looking for “DvTy” key, nil is returned. The existing app simply stopped working properly in the first iOS 17 betas. Is it a bug or the app should be updated now to check for lowercased keys always? FB12309561
5
1
1.1k
Jun ’23
5G CBRS Private Network Support
I am trying to connect an iPad (iOS Beta 17.0 21A5268h) to our lab private 5G SA (Standalone) network. The network is in US and uses CBRS. Other (Android) phones and 5G devices are successfully connected to it. Per the article below, we have tried CBRS PLMN (315-010) and Test PLMN (999-99) without any success. https://support.apple.com/en-ca/guide/deployment/depac6747317/web Basic issues seems to be that the "5G Standalone" toggle on Cellular Data Options menu is greyed out with a message that says "Your current SIM does not support 5G Standalone. Contact your carrier for more information". Trying to choose our own network doesn't list our network (but lists other public 5G networks). Same SIMs work on some Android phones and other 5G devices to connect to this network. If you have successfully connected iPhone or iPad with 5G SA private network (not public carriers), some details/hints will be appreciated.
5
0
1.1k
Jul ’23
How to get the current connected Network Interface in MacOS
I want to ping to IPV6 via local-link address, which needs to get the current active Network Interface like Wi-Fi or Ethernet via adapter connection. Like: ping6 fe80::122b:41ff:feb3:6a20%en0 With en0 is the Wi-Fi Interface. I have tried : private func getAllNetInterfaceName() -> [String] { let interfaces = SCNetworkInterfaceCopyAll() as? [SCNetworkInterface] ?? [] return interfaces.compactMap { SCNetworkInterfaceGetBSDName($0) as? String }.filter { !$0.isEmpty } } It returns the array of the current Interfaces, but I still can not get which one is currently connected. Did you have any clue?
9
0
1.7k
Jul ’23
Alternatives to using en0 for WiFi IP Address lookup
Currently, we have a Flutter app that uses the network_info_plus plugin to connect to the Wifi network of the current iOS device our app is running on. Unfortunately, we are unable to use Bonjour on the target device that we are attempting to connect to, which acts as an AP, due to legacy device issues. Hence the reason why we use the device's Wifi IP address to interact with this device when it's connected to this device's Access Point. This plugin's iOS implementation is using a non-guaranteed way of fetching the device's Wifi IP address according to a recent DevForum post, presuming that it's en0 as seen here: - (void)enumerateWifiAddresses:(NSInteger)family usingBlock:(void (^)(struct ifaddrs *))block { struct ifaddrs *interfaces = NULL; struct ifaddrs *temp_addr = NULL; int success = 0; // retrieve the current interfaces - returns 0 on success success = getifaddrs(&interfaces); if (success == 0) { // Loop through linked list of interfaces temp_addr = interfaces; while (temp_addr != NULL) { if (temp_addr->ifa_addr->sa_family == family) { // en0 is the wifi connection on iOS if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { block(temp_addr); } } temp_addr = temp_addr->ifa_next; } } // Free memory freeifaddrs(interfaces); } I was just wondering what an alternative Objective-C implementation would look like for fetching the actual IP address using a subnet broadcast, since the aforementioned DevForum post suggested that we get all Ethernet-like interfaces (I'm assuming those devices that are prefixed with en) and create a Socket for them bound to IP_BOUND_IF. Thank you in advance.
12
0
962
Jul ’23
Calling BSD Sockets from Swift
I find myself using BSD Sockets a lot. Ironically this isn’t because I’m doing networking. If I’m writing networking code on Apple platforms, I use one of Apple’s preferred APIs, like URLSession or Network framework. For more on this see TN3151 Choosing the right networking API No, I’m using BSD Sockets tangentially, not for my core networking code but for other, weirder stuff. For example, I might want to get a list of all the IP addresses on the system, and that requires calling getifaddrs, which returns its results as struct sockaddr values, which is the BSD Sockets address primitive. Calling BSD Sockets correctly from Swift is a challenge [1]. Over the years I’ve come up with numerous wrappers to make this easier. I’ve found that tricky to do. If I were writing production code I would create a comprehensive wrapper that covers all the details. However, I don’t write production code, I write test projects, snippets for posting to DevForums, and so on. Given that, I want my wrapper to be thin, so that the reader can understand the BSD Sockets concepts without getting lost of the wrapper. Oh, and I don’t really care about efficiency (-: Recently I’ve used my understanding of these goals to create a BSD Sockets wrapper that I’m comfortable sharing. It’s convenient, reasonably ‘thin’, and works well for test projects, snippets, and so on. It can also be wildly inefficient but, as mentioned above, I don’t care about that (-: IMPORTANT TN3151 discusses the circumstances under which it makes sense to use BSD Sockets for networking. Most of these involve calling BSD Sockets from a C-based language rather than Swift, and my wrapper is irrelevant in that case. So, with that out of the way, meet QSocket2023! [2] Well… actually… I’m going to put this in four separate posts: QSocket: Addresses QSocket: DNS QSocket: Interfaces QSocket: System Additions QSocket: I/O QSocket: Socket Options Finally, if you’re using BSD Sockets for networking, you might be interested in Monitoring Socket Viability. If you have any questions or comments about this stuff, please start a new thread here on DevForums. Tag it with Network so that I see it. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] Rumour has it that, even in C, calling BSD Sockets without bumping into undefined behaviour is a challenge, but I’ll let the C language lawyers litigate that (-: [2] That’s right, I’ve written so many BSD Sockets wrappers that I’ve started using the year to distinguish them! Revision History 2023-10-27 Added a link to Monitoring Socket Viability. 2023-07-20 First posted.
0
0
847
Jul ’23
Extra-ordinary Networking
Most apps perform ordinary network operations, like fetching an HTTP resource with URLSession and opening a TCP connection to a mail server with Network framework. These operations are not without their challenges, but they’re the well-trodden path. Note If your app performs ordinary networking, see TN3151 Choosing the right networking API for recommendations as to where to start. Some apps have extra-ordinary networking requirements. For example, apps that: Help the user configure a Wi-Fi accessory Require a connection to run over a specific interface Listen for incoming connections Building such an app is tricky because: Networking is hard in general. Apple devices support very dynamic networking, and your app has to work well in whatever environment it’s running in. Documentation for the APIs you need is tucked away in man pages and doc comments. In many cases you have to assemble these APIs in creative ways. If you’re developing an app with extra-ordinary networking requirements, this post is for you. Note If you have questions or comments about any of the topics discussed here, put them in a new thread here on DevForums. Make sure I see it by tagging it with… well… tags appropriate to the specific technology you’re using, like Foundation, CFNetwork, Network, or Network Extension. Links, Links, and More Links Each topic is covered in a separate post: The iOS Wi-Fi Lifecycle describes how iOS joins and leaves Wi-Fi networks. Understanding this is especially important if you’re building an app that works with a Wi-Fi accessory. Network Interface Concepts explains how Apple platforms manage network interfaces. If you’ve got this far, you definitely want to read this. Network Interface Techniques offers a high-level overview of some of the more common techniques you need when working with network interfaces. Network Interface APIs describes APIs and core techniques for working with network interfaces. It’s referenced by many other posts. Running an HTTP Request over WWAN explains why most apps should not force an HTTP request to run over WWAN, what they should do instead, and what to do if you really need that behaviour. If you’re building an iOS app with an embedded network server, see Showing Connection Information in an iOS Server for details on how to get the information to show to your user so they can connect to your server. Many folks run into trouble when they try to find the device’s IP address, or other seemingly simple things, like the name of the Wi-Fi interface. Don’t Try to Get the Device’s IP Address explains why these problems are hard, and offers alternative approaches that function correctly in all network environments. If you’re building an app that works with a Wi-Fi accessory, see Working with a Wi-Fi Accessory. If you’re trying to gather network interface statistics, see Network Interface Statistics. There are also some posts that are not part of this series but likely to be of interest if you’re working in this space: Local Network Privacy FAQ discusses iOS’s local network privacy feature. Calling BSD Sockets from Swift does what it says on the tin, that is, explain how to call BSD Sockets from Swift. When doing weird things with the network, you often find yourself having to use BSD Sockets, and that API is not easy to call from Swift. The code therein is primarily for the benefit of test projects, oh, and DevForums posts like this one. TN3111 iOS Wi-Fi API overview is a critical resource if you’re doing Wi-Fi specific stuff on iOS. TLS For Accessory Developers tackles the tricky topic of how to communicate securely with a network-based accessory. Networking Resources has links to many other useful resources. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Revision History 2024-04-30 Added a link to Network Interface Statistics. 2023-09-14 Added a link to TLS For Accessory Developers. 2023-07-23 First posted.
0
0
1.3k
Jul ’23
The iOS Wi-Fi Lifecycle
For important background information, read Extra-ordinary Networking before reading this. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" The iOS Wi-Fi Lifecycle Most developers don’t care about about iOS manages its Wi-Fi interface. However, for some apps these details matter. This post gives an overview of that process. IMPORTANT This post covers a lot of ground, and its focus is on concepts. I’ve skipped over a bunch of details, and it’s also quite possible that I’ve got some things wrong. Lemme know if you find any particularly egregious problems. Network Interfaces A typical iPhone has two general-purpose network interfaces: WWAN (cellular) and Wi-Fi. Each of these has their own lifecycle. On modern iPhones [1] the WWAN lifecycle is quite simple: The system tries hard to keep the interface up as much as possible. That’s because important system services, like push notifications, run over WWAN. The story for Wi-Fi is more complex, way more complex. The rest of this post talks about Wi-Fi, and it only really covers the basics. IMPORTANT This post is focused on iOS. The Wi-Fi lifecycle on iPad OS is similar to that of iOS; the main difference is that most iPadOS devices don’t have an WWAN interface. OTOH, the Wi-Fi lifecycle is very different on macOS, tvOS, and watchOS. [1] Historically iOS would bring up the WWAN interface on demand. This is no longer a concern, although on-demand interfaces still exist. See Connect by name in TN3151 Choosing the right networking API. Wi-Fi Lifecycle Basics iOS maintains a list of known Wi-Fi networks. Each of those networks has an auto-join flag, which tells iOS whether it should try to automatically join the network. The user can control this using Settings > Wi-Fi > TheirNetworkName > Auto-Join. There are various circumstances where iOS will try to auto-join a network. The details are not officially documented, but some common examples are: Waking the device by pressing the Side button Bringing a Wi-Fi app to the front A Wi-Fi app is one that that has the UIRequiresPersistentWiFi property set in its Info.plist [1]. Additionally, if the device doesn’t have WWAN, or WWAN is down, iOS will work harder to join a Wi-Fi network in order to maintain important system services like push notifications. There are various circumstances where iOS will leave a Wi-Fi network. Again, the details are not documented but common examples are: Device sleep, which typically happens shortly after screen lock After 30 minutes of not having a Wi-Fi app active Finally, the user can: Manually join a network using Settings > Wi-Fi Temporarily leave a Wi-Fi network using Control Centre [2] Turn Wi-Fi on and off using Settings > Wi-Fi [1] The behaviour of Wi-Fi apps is one of the places where I’m least confident about the accuracy of this post. What I’ve described here is most definitely the behaviour from the early days of iOS. Things may have changed. I confirmed some of the behaviour I’ve described here, but I don’t have time right now to test them all. [2] This does not turn off Wi-Fi. For the details, see Use Bluetooth and Wi-Fi in Control Center. Wi-Fi and the Wider Internet When iOS auto-joins a Wi-Fi network, it evaluates whether the network leads to the wider Internet. If not, it immediately turns around and leaves that network. While it’s doing this evaluation the Wi-Fi interface is up but it can’t become the default route. The exact criteria used for this evaluation is not documented, but there are two obvious requirements: The network must have a viable IP configuration. The network must not be captive. A viable IP configuration typically means that the DHCP server on the network returned an IPv4 address, subnet mask, and router. However, that’s only part of the story. For example: There doesn’t need to be a DHCP server at all! Remember that iOS has a list of known networks, and a known network might have a static IP configuration. There doesn’t need to be an IPv4 address. iOS supports IPv6-only networks. iOS doesn’t do the viable IP evaluation if you manually join a Wi-Fi network. In that case, it’ll stay on that network until it leaves for some other reason, like device sleep. The captive network test involves sending an HTTP request to a server at Apple [1]. The exact details are not documented but the gist is: If the request returns the expected information, the network is not captive. If the request redirects, the network is captive. If the request fails, the system assumes it’s not captive. If the network is identified as a captive network, the system’s captive network support kicks in. This is super complex, far too complex to fully explain here. For more about this, see Hotspot Network Subsystem Programming Guide (and marvel at Figure 1-1!). Note This is another place where iOS treats Wi-Fi apps differently: It’s more likely to bring up the captive network sheet over a Wi-Fi app. However, for the purposes of this discussion, you can think of the entire captive network process as the system taking a very long time to evaluate whether the network leads to the wider Internet. So, the interface remains up but it can’t become the default route. [1] This is the old school test. A network can explicitly advertise its captive status using the techniques described in How to modernize your captive network. Revision History 2023-07-28 Clarified some of the information about Wi-Fi apps. 2023-07-23 First posted.
0
0
693
Jul ’23
Network Interface Concepts
For important background information, read Extra-ordinary Networking before reading this. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Network Interface Concepts Most developers don’t care about how the system manages network interfaces and their addresses. However, for some apps these details matter. This post gives an overview of that process. IMPORTANT This post covers a lot of ground, and its focus is on concepts. I’ve skipped over a bunch of details, and it’s also quite possible that I’ve got some things wrong. Lemme know if you find any particularly egregious problems. Terminological Inexactitude The term interface is overloaded. In general terms we say things like “connect the Ethernet interface”, which refers to a hardware interface. However, at the API level a network interface represents a source and sink of packets for a particular network protocol. That interface may run over a hardware interface, or it may be entirely virtual, like VPN or loopback. Your Network Interface Fallacy Is… Networking on Apple devices is way more complicated than you might think. Consider: A device may have 0 or more network interfaces [1]. A device may have 0 or more network interfaces of a given type. Historically there was an iPhone with no Wi-Fi interface! And an iPhone today might have multiple WWAN interfaces. No single network interface best represents the device as a whole. BSD interface names are not considered API. There’s no guarantee, for example, that an iPhone’s Wi-Fi interface is en0. A device may have 0 or more IP addresses. Which can be an arbitrary mix of IPv4 and IPv6. Each address may or may not be globally reachable. For example, a 192.168.0.0/16 private-use address is not globally reachable. A hardware interface may have 0 or more IP addresses. These can be an arbitrary mix of IPv4 and IPv6. And each one may or may not be link-local. It’s common for a hardware interface to have multiple IPv6 link-local addresses. No single IP address best represents the device as a whole. Even within the scope of a single hardware interface, no single IP address best represents that interface. There’s no correlation between an interface’s type and the presence of a globally reachable IP address. For example, most cellular carriers use NAT, so the device’s WWAN interface gets a private-use address, but some carriers don’t, so it gets a globally reachable IP address! The list of interfaces can change at any time. As can the list of IP addresses. [1] For this discussion I’m ignoring loopback interfaces and their loopback IP addresses. You can rely on those always being present. Address Acquisition When an interface comes up — for example, the user joins a Wi-Fi network or plugs in an Ethernet cable — the system tries to acquire IP addresses for that interface. The exact mechanism for this varies by IP version and interface type. For example, for Ethernet-like interfaces the system typically gets an IPv4 address using DHCP. In contrast, for IPv6 the system starts by assigning at least one link-local address and then uses that to request addresses from the router. There are three common behaviours with respect to IPv4: In some cases the system won’t even try to acquire an IPv4 address for the interface. Apple platforms often have private, internal interfaces. They assign such an interface a link-local IPv6 address. The internal subsystems that use them are expected to support IPv6. In others it’ll try to acquire an IPv4 address but, if that fails, it falls back to a link-local IPv4 address. For more on IPv4 link-local addresses, see RFC 3927 Dynamic Configuration of IPv4 Link-Local Addresses. In other cases it’ll try to acquire an IPv4 address but, if that fails, it’ll take the interface down. Interfaces and the Default Route Apple platforms support multiple network interfaces. One of those interfaces is special in that it holds the default route. That route is used when the system needs to send traffic and there’s no other information about where to send it. In practice, the default route is effectively the path to the wider Internet. As interfaces come up and go down, the system re-evaluates which one should be the default route. This is complicated by two factors: An interface can come up in two stages. In the first stage, the system brings up the interface to evaluate whether it’s useful. A classic example of this is the captive network support on iOS. If that evaluation completes successfully, the system makes the interface eligible to become the default route. In the presence of VPN there may be multiple default routes! If a VPN interface claims the default route, it’ll be the default route for most programs. However, inside the VPN interface’s implementation, the original default route takes precedence. Note Thinking about that second point makes my brain hurt. If you want to join me in the pain, hop on over to A Peek Behind the NECP Curtain. iOS has a general policy of preferring Wi-Fi to WWAN. If the Wi-Fi interface is eligible to the become the default route, iOS makes it so. Many iPhone users think of this as “switching to Wi-Fi”. That’s the general effect, but its not how it actually works. When iOS switches the default route to the Wi-Fi interface, the WWAN interface is still available, it’s just not the default route. In contrast, macOS gives the user control over this. In System Settings > Network there’s an action menu with a Set Service Order command. The resulting window shows the list of all the network interfaces. The user can reorder interfaces in that list. As interfaces come up and down, macOS ensures that the default route is always the first interface that’s eligible. Routing Information The previous section says “and there’s no other information about where to send it”. What other information might there be? This brings us to the system’s routing table. It’s a set of rules that lets the system map traffic to a destination interface. The routing table is directly exposed on macOS, but you can see its effect on all platforms. For example: For each interface there’s a route to that interface’s network. So, imagine your local Wi-Fi network is 192.168.1.0/24. Any traffic destined for that network will be routed directly to that interface, ignoring the default route. You can bind a network operation to a specific interface, ensuring that its traffic will only go out over that interface. Packet tunnel providers can add routes for their VPN interface. On macOS you can dig into the routing table with with tools like: netstat — See its man page. route — See its man page, in section 8. There’s even a routing socket API; see the route man page in section 4. WARNING Routing sockets are not considered API on other Apple platforms. The declarations needed to use them are not part of our other platform SDKs. I’ve seen folks copy the declarations from the macOS SDK and go to town. That is so not supported.
0
0
583
Jul ’23
Network Interface Techniques
For important background information, read Extra-ordinary Networking before reading this. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Network Interface Techniques Most developers don’t care about how the system manages network interfaces and their addresses. However, for some apps these details matter. This post is a high-level overview of some of the more common scenarios. Before you read this, read Network Interface Concepts. Binding to an Interface Our networking APIs let you bind network operations to a specific interface. This triggers an effect known as scoped routing, where the route used by that operation’s traffic is determined by both the combination of its source and destination. There are two ways this can happen: Explicitly, before starting the operation Implicitly, as the operation starts The way you explicitly bind a network operation to an interface varies by API: With URLSession you set up constraints using properties on the URLSessionConfiguration object you use to create the session. For example, the allowsCellularAccess property ensures that operations done by the session will never run over WWAN. With Network framework you set up constraints using properties on the NWParameters object you use to create the NWConnection. For example, the requiredInterface property forces the connection to run over a specific interface. With BSD Sockets, the best way to bind a socket to an interface is with the IP_BOUND_IF (IPv4) or IPV6_BOUND_IF (IPv6) socket options. A network operation can also be bound to an interface implicitly. This is easiest to understand with TCP. A TCP connection is uniquely identified by the local IP / local port / remote IP / remote port tuple [1]. When the system establishes a TCP connection, it decides on a local address. Once it’s done that, the local address can’t change because it’s part of the tuple that literally defines the connection. So, once you make a connection over a specific interface, it continues to run over that interface. Note The connection is bound to the address, not the interface. If the interface’s address changes — for example, if the DHCP server refuses to renew the device’s lease and instead allocates it a new address — the connection will no longer function even though the interface is still available. UDP does not use connections but Apple platforms have a similar concept known as a UDP flow, that is, all datagrams with the same local IP / local port / remote IP / remote port tuple. This concept shows up in a number of places, including: Network framework — When you use NWConnection with UDP, that represents a UDP flow. BSD Sockets — If you create a connected UDP socket, that represents a UDP flow. UDP flows have the same behaviour as TCP connections when it comes to interface address changes. [1] There’s a technology called Multipath TCP that lifts this invariant. Apple platforms lets you opt in to Multipath TCP, assuming your server supports it. A Better Path Consider this scenario: Your app is on an iPhone that’s not connected to Wi-Fi. It establishes a long-lived TCP connection to a server. The iPhone joins a Wi-Fi network, and so the default route changes to Wi-Fi. When happens to that connection? It turns out that, due to scoped routing, the connection continues to work just fine. When you establish the connection, WWAN is the default route and so the connection’s local address is set to that of WWAN. That implicitly binds it to WWAN, so it keeps working regardless of the default route change in step 3. This is both a good and bad thing. It’s good because the connection keeps working. It’s bad because the user might download a huge amount of data thinking that they’re on Wi-Fi. If you’re building an app like this, check out the betterPathUpdateHandler property in Network framework. This is called when a better path is available. You can then: Start up a new connection, which will use the better path. Once that’s in place, gracefully close the old connection. Constraints The above example illustrates an important point: The network state can change at any time and it’s hard to stay in sync. Imagine you plan to use a connection to download a large resource. If you use a reactive approach, as described above, you might end up accidentally doing that over WWAN. A better way to handle this is to declare constraints on your network operations. In Network framework, for example, you can set things like the prohibitConstrainedPaths property and the prohibitExpensivePaths property in the NWParameters object you use to create your connection. The system enforces those constraints for you, so you don’t have to write extra code to respond to network state changes. These properties have a second benefit: The user may have a different definition of expensive than you. For example: Some users have unlimited WWAN data plans and are never on Wi-Fi, so WWAN is not expensive. The user’s iPad might be connected to a Personal Hotspot published by their iPhone, making Wi-Fi expensive. If you use these constraints, your app will automatically honour the user’s settings, just like Apple’s built-in apps. URLSession has similar facilities in the URLSessionConfiguration object, namely the allowsConstrainedNetworkAccess property and the allowsExpensiveNetworkAccess property. [Sorry that the names are inverted relative to Network framework, but the different polarities make sense in the context of the containing object.] The documentation for the URLSession properties is currently much better than that for the Network framework properties, so start there even if you’re using Network framework. And for a general introduction to these constraints, watch WWDC 2019 Session 712 Advances in Networking, Part 1.
0
0
606
Jul ’23
Network Interface APIs
For important background information, read Extra-ordinary Networking before reading this. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Network Interface APIs Most developers don’t need to interact directly with network interfaces. If you do, read this post for a summary of the APIs available to you. Before you read this, read Network Interface Concepts. Interface List The standard way to get a list of interfaces and their addresses is getifaddrs. To learn more about this API, see its man page. A network interface has four fundamental attributes: A set of flags — These are packed into a CUnsignedInt. The flags bits are declared in <net/if.h>, starting with IFF_UP. An interface type — See Network Interface Type, below. An interface index — Valid indexes are greater than 0. A BSD interface name. For example, an Ethernet interface might be called en0. The interface name is shared between multiple network interfaces running over a given hardware interface. For example, IPv4 and IPv6 running over that Ethernet interface will both have the name en0. WARNING BSD interface names are not considered API. There’s no guarantee, for example, that an iPhone’s Wi-Fi interface is en0. You can map between the last two using if_indextoname and if_nametoindex. See the if_indextoname man page for details. An interface may also have address information. If present, this always includes the interface address (ifa_addr) and the network mask (ifa_netmask). In addition: Broadcast-capable interfaces (IFF_BROADCAST) have a broadcast address (ifa_broadaddr, which is an alias for ifa_dstaddr). Point-to-point interfaces (IFF_POINTOPOINT) have a destination address (ifa_dstaddr). Calling getifaddrs from Swift is a bit tricky. For an example of this, see QSocket: Interfaces. IP Address List Once you have getifaddrs working, it’s relatively easy to manipulate the results to build a list of just IP addresses, a list of IP addresses for each interface, and so on. QSocket: Interfaces has some Swift snippets that show this. Interface List Updates The interface list can change over time. Hardware interfaces can be added and removed, network interfaces come up and go down, and their addresses can change. It’s best to avoid caching information from getifaddrs. If thats unavoidable, use the kNotifySCNetworkChange Darwin notification to update your cache. For information about registering for Darwin notifications, see the notify man page (in section 3). This notification just tells you that something has changed. It’s up to you to fetch the new interface list and adjust your cache accordingly. You’ll find that this notification is sometimes posted numerous times in rapid succession. To avoid unnecessary thrashing, debounce it. While the Darwin notification API is easy to call from Swift, Swift does not import kNotifySCNetworkChange. To fix that, define that value yourself, calling a C function to get the value: var kNotifySCNetworkChange: UnsafePointer<CChar> { networkChangeNotifyKey() } Here’s what that C function looks like: extern const char * networkChangeNotifyKey(void) { return kNotifySCNetworkChange; } Network Interface Type There are two ways to think about a network interface’s type. Historically there were a wide variety of weird and wonderful types of network interfaces. The following code gets this legacy value for a specific BSD interface name: func legacyTypeForInterfaceNamed(_ name: String) -> UInt8? { var addrList: UnsafeMutablePointer<ifaddrs>? = nil let err = getifaddrs(&addrList) // In theory we could check `errno` here but, honestly, what are gonna // do with that info? guard err >= 0, let first = addrList else { return nil } defer { freeifaddrs(addrList) } return sequence(first: first, next: { $0.pointee.ifa_next }) .compactMap { addr in guard let nameC = addr.pointee.ifa_name, name == String(cString: nameC), let sa = addr.pointee.ifa_addr, sa.pointee.sa_family == AF_LINK, let data = addr.pointee.ifa_data else { return nil } return data.assumingMemoryBound(to: if_data.self).pointee.ifi_type } .first } The values are defined in <net/if_types.h>, starting with IFT_OTHER. However, this value is rarely useful because many interfaces ‘look like’ Ethernet and thus have a type of IFT_ETHER. Network framework has the concept of an interface’s functional type. This is an indication of how the interface fits into the system. There are two ways to get an interface’s functional type: If you’re using Network framework and have an NWInterface value, get the type property. If not, call ioctl with a SIOCGIFFUNCTIONALTYPE request. The return values are defined in <net/if.h>, starting with IFRTYPE_FUNCTIONAL_UNKNOWN. Swift does not import SIOCGIFFUNCTIONALTYPE, so it’s best to write this code in a C: extern uint32_t functionalTypeForInterfaceNamed(const char * name) { int fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { return IFRTYPE_FUNCTIONAL_UNKNOWN; } struct ifreq ifr = {}; strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); bool success = ioctl(fd, SIOCGIFFUNCTIONALTYPE, &ifr) >= 0; int junk = close(fd); assert(junk == 0); if ( ! success ) { return IFRTYPE_FUNCTIONAL_UNKNOWN; } return ifr.ifr_ifru.ifru_functional_type; }
0
0
903
Jul ’23
Running an HTTP Request over WWAN
For important background information, read Extra-ordinary Networking before reading this. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Running an HTTP Request over WWAN URLSessionConfiguration has a property, allowsCellularAccess, that controls whether requests in that session are allowed to run over WWAN. However, there’s no opposite of that, a way to force requests to run over WWAN. Some apps need to do this. IMPORTANT There are good and bad reasons to force a request to run over WWAN. I’ll explain a good reason below, but a bad reason is that you think that using WWAN is better aligned with your user’s networking preferences. Don’t go down that path. Rather, use the constraints mechanism described in Network Interface Techniques to align with the user’s preferences. So what’s an example of a good reason for this? I once worked with a developer who’d cut a deal with a cellular carrier to offer special benefits to that carrier’s users. Rather than require that the user give the app access to their cellular account, they wanted to run an HTTP request over WWAN to a server that’s only available on the carrier’s network. If you’re using Network framework, or even BSD Sockets, this isn’t a problem. For information about how to bind a connection to an interface, see Network Interface Techniques. If you’re using URLSession, there’s no obvious way to achieve this goal, but there are two non-obvious approaches you can try: Implement your own HTTP protocol Fun with Multipath TCP! Regarding the first approach, see the discussion in the HTTP alternatives section of TN3151 Choosing the right networking API. Regarding the second approach, the basic idea is to set up an HTTP server with Multipath TCP (MPTCP) support on the carrier’s network. If your app issues a request to that server while both Wi-Fi and WWAN are active, the system will try to connect over both interfaces. The Wi-Fi connection will fail but the WWAN connection will succeed, and thus the request runs over WWAN. Neat-o! For information on how to enable MPTCP with URLSession, see Supporting Multipath TCP.
0
0
454
Jul ’23
Showing Connection Information in an iOS Server
For important background information, read Extra-ordinary Networking before reading this. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Showing Connection Information in an iOS Server A common pattern for iOS apps is to implement an embedded HTTP server in the app that allows the user to interact with it over the network. For example, a comic book reader app might have a web server that let’s the user upload comics to the app using Safari. The networking aspect of this is fairly straightforward. You don’t need to do anything extra-ordinary with network interfaces and so on. Rather, start an ordinary listener and register it with Bonjour. In Network framework that involves setting the service property on the listener, and there are equivalent techniques in other APIs. Where things get complicated is in the UI; or, more accurately, in determining the information to display in the UI. An app like this typically wants to display three bits of information to the user: Bonjour service name, like Guy Smiley — If the user has a Bonjour-aware browser [1], this is by far the easiest way to connect. Local DNS name, like guy-smiley.local. — This works in virtually all browsers on all platforms, and is relatively easy for the user to enter. IP addresses — Typing IP addresses is very old school, but sometimes that’s the user’s only option. The following sections explain how to get each of these items. [1] Sadly, that doesn’t include Safari. Bonjour Service Name All Bonjour APIs have a mechanism to tell you about the name that they used to register your service. For example, with Network framework, the listener has a serviceRegistrationUpdateHandler property. The listener calls that closure with status information about the registration, including the service name. IMPORTANT Bonjour automatically renames your service if it discovers a name conflict. Write your code to handle that case, updating your UI if the service name changes. Local DNS Name Getting the local DNS name on iOS is tricky. The best option is to run a Bonjour service resolution query against your own service name. You can’t use standard service resolution APIs, like DNSServiceResolve, because those stop when the resolution completes, and you want to continue monitoring for changes. Rather, use the DNSServiceQueryRecord function to start an ongoing query for the service’s SRV record. Restrict this query to the local interface (kDNSServiceInterfaceIndexLocalOnly) to avoid generating unnecessary traffic on the ‘wire’. Note macOS makes this much easier because you can use System Configuration framework to get the local DNS name. SCDynamicStoreCopyLocalHostName returns the current value. To learn about changes, use the dynamic store’s notification mechanism to watch for changes to the Setup:/System key. IP Addresses As discussed in Network Interface Concepts, there is no single IP address you can display here. Rather, you have to be prepared to display multiple addresses. To get this list of IP addresses: Get a list of all IP addresses, grouped by interface. Filter that list for interfaces where the functional type is either Wi-Fi or wired. Filter it again for interfaces that have at least one IPv4 address. Merge those lists. This list may include IPv6 addresses. Those are hard to display and even harder for the user to type. Consider whether or not to show them in your UI. Note What about IPv6-only interfaces, I hear you ask? It turns out that for Wi-Fi and wired interfaces that the user cares about, the system will assign a link-local IPv4 address to the interface. So, step 3 is effectively filtering for interfaces that the user cares about. Neat, eh? For information about the specific APIs to use here, see Network Interface APIs. IMPORTANT Update your UI when the IP address list changes. Use kNotifySCNetworkChange to drive this update. See Network Interface APIs for the details. Note This approach only makes sense for iOS and its child platforms. On macOS, use System Configuration framework for this: Use an SCNetworkInterface object to get a list of network interfaces and their various properties. Use an SCDynamicStore object to get the IP address list and monitor for changes.
0
0
603
Jul ’23
Working with a Wi-Fi Accessory
For important background information, read Extra-ordinary Networking before reading this. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Working with a Wi-Fi Accessory Building an app that works with a Wi-Fi accessory presents specific challenges. This post discusses those challenges and some recommendations for how to address them. Note While my focus here is iOS, much of the info in this post applies to all Apple platforms. IMPORTANT iOS 18, currently in beta, includes AccessorySetupKit, a new framework that can radically simplify the process of setting up an accessory. I’m not fully up to speed on that framework myself, but I encourage you to watch WWDC 2024 Session 10203 Meet AccessorySetupKit and read the framework documentation. Accessory Categories I classify Wi-Fi accessories into three different categories. A bound accessory is ultimately intended to join the user’s Wi-Fi network. It may publish its own Wi-Fi network during the setup process, but the goal of that process is to get the accessory on to the existing network. Once that’s done, your app interacts with the accessory using ordinary networking APIs. An example of a bound accessory is a Wi-Fi capable printer. A stand-alone accessory publishes a Wi-Fi network at all times. An iOS device joins that network so that your app can interact with it. The accessory never provides access to the wider Internet. An example of a stand-alone accessory is a video camera that users take with them into the field. You might want to write an app that joins the camera’s network and downloads footage from it. A gateway accessory is one that publishes a Wi-Fi network that provides access to the wider Internet. Your app might need to interact with the accessory during the setup process, but after that it’s useful as is. An example of this is a Wi-Fi to WWAN gateway. Not all accessories fall neatly into these categories. Indeed, some accessories might fit into multiple categories, or transition between categories. Still, I’ve found these categories to be helpful when discussing various accessory integration challenges. Do You Control the Firmware? The key question here is Do you control the accessory’s firmware? If so, you have a bunch of extra options that will make your life easier. If not, you have to adapt to whatever the accessory’s current firmware does. Simple Improvements If you do control the firmware, I strongly encourage you to: Support IPv6 Implement Bonjour [1] These two things are quite easy to do — most embedded platforms support them directly, so it’s just a question of turning them on — and they will make your life significantly easier: Link-local addresses are intrinsic to IPv6, and IPv6 is intrinsic to Apple platforms. If your accessory supports IPv6, you’ll always be able to communicate with it, regardless of how messed up the IPv4 configuration gets. Similarly, if you support Bonjour, you’ll always be able to find your accessory on the network. [1] Bonjour is an Apple term for three Internet standards: RFC 3927 Dynamic Configuration of IPv4 Link-Local Addresses RFC 6762 Multicast DNS RFC 6763 DNS-Based Service Discovery WAC For a bound accessory, support Wireless Accessory Configuration (WAC). This is a relatively big ask — supporting WAC requires you to join the MFi Program — but it has some huge benefits: You don’t need to write an app to configure your accessory. The user will be able to do it directly from Settings. If you do write an app, you can use the EAWiFiUnconfiguredAccessoryBrowser class to simplify your configuration process. HomeKit For a bound accessory that works in the user’s home, consider supporting HomeKit. This yields the same onboarding benefits as WAC, and many other benefits as well. Also, you can get started with the HomeKit Open Source Accessory Development Kit (ADK). Bluetooth LE If your accessory supports Bluetooth LE, think about how you can use that to improve your app’s user experience. For an example of that, see SSID Scanning, below. Claiming the Default Route, Or Not? If your accessory publishes a Wi-Fi network, a key design decision is whether to stand up enough infrastructure for an iOS device to make it the default route. IMPORTANT To learn more about how iOS makes the decision to switch the default route, see The iOS Wi-Fi Lifecycle and Network Interface Concepts. This decision has significant implications. If the accessory’s network becomes the default route, most network connections from iOS will be routed to your accessory. If it doesn’t provide a path to the wider Internet, those connections will fail. That includes connections made by your own app. Note It’s possible to get around this by forcing your network connections to run over WWAN. See Binding to an Interface in Network Interface Techniques and Running an HTTP Request over WWAN. Of course, this only works if the user has WWAN. It won’t help most iPad users, for example. OTOH, if your accessory’s network doesn’t become the default route, you’ll see other issues. iOS will not auto-join such a network so, if the user locks their device, they’ll have to manually join the network again. In my experience a lot of accessories choose to become the default route in situations where they shouldn’t. For example, a bound accessory is never going to be able to provide a path to the wider Internet so it probably shouldn’t become the default route. However, there are cases where it absolutely makes sense, the most obvious being that of a gateway accessory. Acting as a Captive Network, or Not? If your accessory becomes the default route you must then decide whether to act like a captive network or not. IMPORTANT To learn more about how iOS determines whether a network is captive, see The iOS Wi-Fi Lifecycle. For bound and stand-alone accessories, becoming a captive network is generally a bad idea. When the user joins your network, the captive network UI comes up and they have to successfully complete it to stay on the network. If they cancel out, iOS will leave the network. That makes it hard for the user to run your app while their iOS device is on your accessory’s network. In contrast, it’s more reasonable for a gateway accessory to act as a captive network. SSID Scanning Many developers think that TN3111 iOS Wi-Fi API overview is lying when it says: iOS does not have a general-purpose API for Wi-Fi scanning It is not. Many developers think that the Hotspot Helper API is a panacea that will fix all their Wi-Fi accessory integration issues, if only they could get the entitlement to use it. It will not. Note this comment in the official docs: NEHotspotHelper is only useful for hotspot integration. There are both technical and business restrictions that prevent it from being used for other tasks, such as accessory integration or Wi-Fi based location. Even if you had the entitlement you would run into these technical restrictions. The API was specifically designed to support hotspot navigation — in this context hotspots are “Wi-Fi networks where the user must interact with the network to gain access to the wider Internet” — and it does not give you access to on-demand real-time Wi-Fi scan results. Many developers look at another developer’s app, see that it’s displaying real-time Wi-Fi scan results, and think there’s some special deal with Apple that’ll make that work. There is not. In reality, Wi-Fi accessory developers have come up with a variety of creative approaches for this, including: If you have a bound accessory, you might add WAC support, which makes this whole issue go away. You might build your accessory with a barcode containing the info required to join its network, and scan that from your app. This is the premise behind the Configuring a Wi-Fi Accessory to Join the User’s Network sample code. You might configure all your accessories to have a common SSID prefix, and then take advantage of the prefix support in NEHotspotConfigurationManager. See Programmatically Joining a Network, below. You might have your app talk to your accessory via some other means, like Bluetooth LE, and have the accessory scan for Wi-Fi networks and return the results. Programmatically Joining a Network Network Extension framework has an API, NEHotspotConfigurationManager, to programmatically join a network, either temporarily or as a known network that supports auto-join. For the details, see Wi-Fi Configuration. One feature that’s particularly useful is it’s prefix support, allowing you to create a configuration that’ll join any network with a specific prefix. See the init(ssidPrefix:) initialiser for the details. For examples of how to use this API, see: Configuring a Wi-Fi Accessory to Join the User’s Network — It shows all the steps for one approach for getting a non-WAC bound accessory on to the user’s network. NEHotspotConfiguration Sample — Use this to explore the API in general. Secure Communication Users expect all network communication to be done securely. For some ideas on how to set up a secure connection to an accessory, see TLS For Accessory Developers. Revision History 2024-07-16 Added a preliminary discussion of AccessorySetupKit. 2023-10-11 Added the HomeKit section. Fixed the link in Secure Communication to point to TLS For Accessory Developers. 2023-07-23 First posted.
0
0
693
Jul ’23
Don’t Try to Get the Device’s IP Address
For important background information, read Extra-ordinary Networking before reading this. Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" Don’t Try to Get the Device’s IP Address I regularly see questions like: How do I find the IP address of the device? How do I find the IP address of the Wi-Fi interface? How do I identify the Wi-Fi interface? I also see a lot of really bad answers to these questions. That’s understandable, because the questions themselves don’t make sense. Networking on Apple platforms is complicated and many of the things that are ‘obviously’ true are, in fact, not true at all. For example: There’s no single IP address that represents the device, or an interface. A device can have 0 or more interfaces, each of which can have 0 or more IP addresses, each of which can be IPv4 and IPv6. A device can have multiple interfaces of a given type. It’s common for iPhones to have multiple WWAN interfaces, for example. It’s not possible to give a simple answer to any of these questions, because the correct answer depends on the context. Why do you need this particular information? What are you planning to do with it? This post describes the scenarios I most commonly encounter, with my advice on how to handle each scenario. IMPORTANT BSD interface names, like en0, are not considered API. There’s no guarantee, for example, that an iPhone’s Wi-Fi interface is en0. If you write code that relies on a hard-coded interface name, it will fail in some situations. Service Discovery Some folks want to identify the Wi-Fi interface so that they can run a custom service discovery protocol over it. Before you do that, I strongly recommend that you look at Bonjour. This has a bunch of advantages: It’s an industry standard [1]. It’s going to be more efficient on the ‘wire’. You don’t have to implement it yourself, you can just call an API [2]. For information about the APIs available, see TN3151 Choosing the right networking API. If you must implement your own service discovery protocol, don’t think in terms of finding the Wi-Fi interface. Rather, write your code to work with all Wi-Fi interfaces, or perhaps even all Ethernet-like interfaces. That’s what Apple’s Bonjour implementation does, and it means that things will work in odd situations [3]. To find all Wi-Fi interfaces, get the interface list and filter it for ones with the Wi-Fi functional type. To find all broadcast capable interfaces, get the interface list and filter it for interfaces with the IFF_BROADCAST flag set. If the service you’re trying to discover only supports IPv4, filter out any IPv6-only interfaces. For advice on how to do this, see Interface List and Network Interface Type in Network Interface APIs. When working with multiple interfaces, it’s generally a good idea to create a socket per interface and then bind that socket to the interface. That ensures that, when you send a packet, it’ll definitely go out the interface you expect. [1] Bonjour is an Apple term for: RFC 3927 Dynamic Configuration of IPv4 Link-Local Addresses RFC 6762 Multicast DNS RFC 6763 DNS-Based Service Discovery [2] That’s true even on non-Apple platforms. It’s even true on most embedded platforms. If you’re talking to a Wi-Fi accessory, see Working with a Wi-Fi Accessory. [3] Even if the service you’re trying to discover can only be found on Wi-Fi, it’s possible for a user to have their iPhone on an Ethernet that’s bridged to a Wi-Fi. Why on earth would they do that? Well, security, of course. Some organisations forbid their employees from using Wi-Fi. Logging and Diagnostics Some folks want to log the IP address of the Wi-Fi interface, or the WWAN, or both for diagnostic purposes. This is quite feasible, with the only caveat being there may be multiple interfaces of each type. To find all interfaces of a particular type, get the interface list and filter it for interfaces with that functional type. See Interface List and Network Interface Type in Network Interface APIs. Interface for an Outgoing Connection There are situations where you need to get the interface used by a particular connection. A classic example of that is FTP. When you set up a transfer in FTP, you start with a control connection to the FTP server. You then open a listener and send its IP address and port to the FTP server over your control connection. What IP address should you use? There’s an easy answer here: Use the local IP address for the control connection. That’s the one that the server is most likely to be able to connect to. To get the local address of a connection: In Network framework, first get the currentPath property and then get its localEndpoint property. In BSD Sockets, use getsockname. See its man page for details. Now, this isn’t a particularly realistic example. Most folks don’t use FTP these days [1] but, even if they do, they use FTP passive mode, which avoids the need for this technique. However, this sort of thing still does come up in practice. I recently encountered two different variants of the same problem: One developer was implementing VoIP software and needed to pass the devices IP address to their VoIP stack. The best IP address to use was the local IP address of their control connection to the VoIP server. A different developer was upgrading the firmware of an accessory. They do this by starting a server within their app and sending a command to the accessory to download the firmware from that server. Again, the best IP address to use is the local address of the control connection. [1] See the discussion in TN3151 Choosing the right networking API. Listening for Connections If you’re listening for incoming network connections, you don’t need to bind to a specific address. Rather, listen on all local addresses. In Network framework, this is the default for NWListener. In BSD Sockets, set the address to INADDR_ANY (IPv4) or in6addr_any (IPv6). If you only want to listen on a specific interface, don’t try to bind to that interface’s IP address. If you do that, things will go wrong if the interface’s IP address changes. Rather, bind to the interface itself: In Network framework, set either the requiredInterfaceType property or the requiredInterface property on the NWParameters you use to create your NWListener. In BSD Sockets, set the IP_BOUND_IF (IPv4) or IPV6_BOUND_IF (IPv6) socket option. How do you work out what interface to use? The standard technique is to get the interface list and filter it for interfaces with the desired functional type. See Interface List and Network Interface Type in Network Interface APIs. Remember that their may be multiple interfaces of a given type. If you’re using BSD Sockets, where you can only bind to a single interface, you’ll need to create multiple listeners, one for each interface. Listener UI Some apps have an embedded network server and they want to populate a UI with information on how to connect to that server. This is a surprisingly tricky task to do correctly. For the details, see Showing Connection Information for a Local Server. Outgoing Connections In some situations you might want to force an outgoing connection to run over a specific interface. There are four common cases here: Set the local address of a connection [1]. Force a connection to run over a specific interface. Force a connection to run over a type of interface. Force a connection to run over an interface with specific characteristics. For example, you want to download some large resource without exhausting the user’s cellular data allowance. The last case should be the most common — see the Constraints section of Network Interface Techniques — but all four are useful in specific circumstances. The following sections explain how to tackle these tasks in the most common networking APIs. [1] This implicitly forces the connection to use the interface with that address. For an explanation as to why, see the discussion of scoped routing in Network Interface Techniques. Network Framework Network framework has good support for all of these cases. Set one or more of the following properties on the NWParameters object you use to create your NWConnection: requiredLocalEndpoint property requiredInterface property prohibitedInterfaces property requiredInterfaceType property prohibitedInterfaceTypes property prohibitConstrainedPaths property prohibitExpensivePaths property Foundation URL Loading System URLSession has fewer options than Network framework but they work in a similar way: Set one or more of the following properties on the URLSessionConfiguration object you use to create your session: allowsCellularAccess property allowsConstrainedNetworkAccess property allowsExpensiveNetworkAccess property Note While these session configuration properties are also available on URLRequest, it’s better to configure this on the session. There’s no option that forces a connection to run over a specific interface. In most cases you don’t need this — it’s better to use the allowsConstrainedNetworkAccess and allowsExpensiveNetworkAccess properties — but there are some situations where that’s necessary. For advice on this front, see Running an HTTP Request over WWAN. BSD Sockets BSD Sockets has very few options in this space. One thing that’s easy and obvious is setting the local address of a connection: Do that by passing the address to bind. Alternatively, to force a connection to run over a specific interface, set the IP_BOUND_IF (IPv4) or IPV6_BOUND_IF (IPv6) socket options.
0
0
1.1k
Jul ’23
Hidden fields / annotations aren't hidden
I'm using PDFKit to show a PDF which contains some hidden fields. Unfortunately, PDFKit seems to ignore that property, annotation.shouldDisplay is always YES. I have to set shouldDisplay by my own depending on the annotation's internal flags: // see Chapter 8.4.2 Annotation Flags in PDF Reference for PDF 1.7 // https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/pdfreference1.7old.pdf static const NSUInteger PDFAnnotationFlagInvisible = 1; static const NSUInteger PDFAnnotationFlagHidden = 1 << 1; // ... for (PDFAnnotation* annotation in page.annotations) { id value = [annotation valueForAnnotationKey:PDFAnnotationKeyFlags]; if (value != nil) { NSInteger annotationFlags = [value integerValue]; if (annotationFlags & (PDFAnnotationFlagInvisible | PDFAnnotationFlagHidden)) { annotation.shouldDisplay = NO; } } } It doesn't feel right that this snippet is needed. So is this a bug / known issue in PDFKit or is my PDF somehow "wrong".
1
0
648
Jul ’23