Network connections send and receive data using transport and security protocols.

Posts under Network tag

200 Posts

Post

Replies

Boosts

Views

Activity

Networking Resources
General: Forums subtopic: App & System Services > Networking TN3151 Choosing the right networking API Networking Overview document — Despite the fact that this is in the archive, this is still really useful. TLS for App Developers forums post Choosing a Network Debugging Tool documentation WWDC 2019 Session 712 Advances in Networking, Part 1 — This explains the concept of constrained networking, which is Apple’s preferred solution to questions like How do I check whether I’m on Wi-Fi? TN3135 Low-level networking on watchOS TN3179 Understanding local network privacy Adapt to changing network conditions tech talk TCP and UDP ports used by Apple software products support article Understanding Also-Ran Connections forums post Extra-ordinary Networking forums post Foundation networking: Forums tags: Foundation, CFNetwork URL Loading System documentation — NSURLSession, or URLSession in Swift, is the recommended API for HTTP[S] on Apple platforms. Moving to Fewer, Larger Transfers forums post Testing Background Session Code forums post Network framework: Forums tag: Network Network framework documentation — Network framework is the recommended API for TCP, UDP, and QUIC on Apple platforms. WWDC 2025 Session 250 Use structured concurrency with Network framework — This is a great introduction to the new Network framework API introduced in appleOS 2026. Building a custom peer-to-peer protocol sample code (aka TicTacToe) Implementing netcat with Network Framework sample code (aka nwcat) Configuring a Wi-Fi accessory to join a network sample code Moving from Multipeer Connectivity to Network Framework forums post NWEndpoint History and Advice forums post Wi-Fi (general): How to modernize your captive network developer news post Wi-Fi Fundamentals forums post Filing a Wi-Fi Bug Report forums post Working with a Wi-Fi Accessory forums post — This is part of the Extra-ordinary Networking series. Wi-Fi (iOS): TN3111 iOS Wi-Fi API overview technote Wi-Fi Aware framework documentation Building peer-to-peer apps sample code WirelessInsights framework documentation iOS Network Signal Strength forums post Network Extension Resources Wi-Fi on macOS: Forums tag: Core WLAN Core WLAN framework documentation Secure networking: Forums tags: Security Apple Platform Security support document Preventing Insecure Network Connections documentation — This is all about App Transport Security (ATS). WWDC 2017 Session 701 Your Apps and Evolving Network Security Standards [1] — This is generally interesting, but the section starting at 17:40 is, AFAIK, the best information from Apple about how certificate revocation works on modern systems. WWDC 2025 Session 314 Get ahead with quantum-secure cryptography Available trusted root certificates for Apple operating systems support article Requirements for trusted certificates in iOS 13 and macOS 10.15 support article About upcoming limits on trusted certificates support article Apple’s Certificate Transparency policy support article What’s new for enterprise in iOS 18 support article — This discusses new key usage requirements. Prepare your network environment for stricter security requirements support article — This is primarily of interest to folks developing management software, for example, an MDM server. Technote 2232 HTTPS Server Trust Evaluation Technote 2326 Creating Certificates for TLS Testing QA1948 HTTPS and Test Servers Miscellaneous: More network-related forums tags: 5G, QUIC, Bonjour On FTP forums post Using the Multicast Networking Additional Capability forums post Investigating Network Latency Problems forums post Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] This video is no longer available from Apple, but the URL should help you locate other sources of this info.
0
0
6.7k
Sep ’26
tvOS 14.6 fails trying to bind UDP/1900 (succeeds on tvOS Simulator)
I'm working on a tvOS app/library which uses SSDP. The code binds a UDP socket to port 1900 and joins a multicast group using BSD Socket library. I believe this used to work on a previous tvOS version, but specifically as of tvOS 14.6 there is a divergence between the tvOS Simulator and actual devices. Actual devices fail at the call to bind, with an Address Already in Use error. The tvOS simulator binds just fine after throwing up a macOS firewall 'allow incoming connections' dialog. Any ideas what the official situation is here? The com.apple.developer.networking.multicast entitlement seems to be iOS only at this time, is that correct? Also there seems to be no 'local networking' permission dialog in tvOS - is that also correct?
5
0
1.4k
15h
NWConnection/NWListener: .failed, .cancelled, and State Handler Semantics
Hi, I am using Apple's Network.framework APIs, specifically NWConnection and NWListener over UDP, in my project. Both APIs use state-update handlers, and I would like to clarify a few aspects of their lifecycle and callback-delivery semantics. Are .failed and .cancelled delivered only once during the lifecycle? For a given NWConnection or NWListener instance, my understanding is that .failed is a terminal state and the object does not recover from it. Is .failed guaranteed to be delivered at most once during the lifetime of an instance? If the object enters .failed, can I call cancel() on that same instance? If so, will the state-update handler subsequently receive .cancelled? Is .cancelled guaranteed to be delivered at most once per instance, even if cancel() is called multiple times? If the object is in the .waiting state and I call cancel(), causing it to transition to .cancelled, is it guaranteed that .failed will not subsequently be delivered for that instance? Since there is no transition from .cancelled -> .failed? What happens when stateUpdateHandler is replaced while callbacks are pending? Consider the following scenario: I create an NWConnection and assign a stateUpdateHandler (Handler A). The connection's state changes, and a callback for Handler A is enqueued on the connection's associated DispatchQueue. Before that queued callback executes, I replace stateUpdateHandler with Handler B. If the callback for Handler A was already enqueued before the handler was replaced, will Handler A still be invoked? My understanding is Yes, since it was already queued. If the object is in the .waiting state and I call cancel(), causing it to transition to .cancelled, is it guaranteed that .failed will not subsequently be delivered for that instance Can the same state notification be delivered to both Handler A and Handler B, or is it delivered to only one of them i.e., Handler A, since during the state-change Handler A was the one as stateUpdateHandler? Thanks!
3
1
338
1d
Is Data.withUnsafeMutableBytes(_:) safe to rely on long-term for C++ interop?
Hi, in our project we use Network.framework, with NWListener and NWConnection, to send and receive data over UDP. We use the receiveMessage(completion:) API, which delivers the data as a Data object. We assign this Data to a property of one of our classes, which we continue to work with after the completion handler returns. We pass that property's bytes to our C++ code (through Swift–C++ interop) using Data.withUnsafeMutableBytes(_:). The C++ code modifies the bytes in place, and we then continue working with the modified Data in Swift. Since nothing else refers to the Data we received, the bytes are modified in place and no copy is made. However, if another Data value shared the same buffer, just calling withUnsafeMutableBytes(_:) would create a copy, whether or not we actually modified the bytes. Our understanding is that this is because the method is declared mutating, so the call itself is treated as a write and copy-on-write makes the buffer unique before the pointer is handed out. We also understand that ResultType : ~Copyable (along with the typed throws(E)) only relaxes the requirements on the closure's return type and doesn't affect this behaviour. We understand this approach is unsafe and that the pointer "should not be stored and used outside of the lifetime of the call to the closure", so we will take care of that. Since the typed-pointer variants of APIs such as Data.withUnsafeBytes were deprecated in Swift 5, we would also like to confirm that withUnsafeMutableBytes(_:), the variant that takes an UnsafeMutableRawBufferPointer, is the intended long-term API and isn't expected to be deprecated in the same way.
5
1
208
1d
Crash in libquic.dylib | quic_recovery_pto | iOS 26.1
Hello, I am investigating a recurring crash that appears to be originating within the system's network stack. OS Version: iPhone OS 26.1 (23B85) Role: Foreground Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000 Exception Codes: 0x0000000000000001, 0x0000000000000000 Triggered by Thread: 19 Description: The crash is triggered by Thread 19 and occurs deep within libquic.dylib during a QUIC recovery timer event. Based on the backtrace, the failure happens in quic_recovery_pto. The issue seems to occur when a protocol instance schedules a wakeup, leading to a null pointer dereference in the system library. Crashed Thread Backtrace snippet:Thread 19 Crashed: Thread 19 Crashed: 0 libquic.dylib 0x00000001a00a38cc quic_recovery_pto + 72 (quic_recovery.c:1259) 1 libquic.dylib 0x00000001a00a3390 quic_recovery_timer_fired + 132 (quic_recovery.c:1460) 2 libquic.dylib 0x00000001a00a1f8c quic_timer_run + 248 (quic_timer.c:210) 3 Network 0x000000018ec76cbc __nw_protocol_instance_schedule_wakeup_block_invoke + 76 (protocol_implementation.cpp:5847) 4 Network 0x000000018eba34e0 __nw_context_reset_timer_block_with_time_block_invoke + 268 (context.cpp:2224) 5 libdispatch.dylib 0x00000001c84727ec _dispatch_client_callout + 16 (client_callout.mm:85) 6 libdispatch.dylib 0x00000001c845d664 _dispatch_continuation_pop + 596 (queue.c:349) 7 libdispatch.dylib 0x00000001c8470528 _dispatch_source_latch_and_call + 396 (source.c:601) 8 libdispatch.dylib 0x00000001c846f1fc _dispatch_source_invoke + 844 (source.c:966) 9 libdispatch.dylib 0x00000001c8463288 _dispatch_workloop_invoke + 1612 (queue.c:4761) 10 libdispatch.dylib 0x00000001c846c3ec _dispatch_root_queue_drain_deferred_wlh + 292 (queue.c:7265) 11 libdispatch.dylib 0x00000001c846bce4 _dispatch_workloop_worker_thread + 692 (queue.c:6859) 12 libsystem_pthread.dylib 0x00000001ec0623b8 _pthread_wqthread + 292 (pthread.c:2696) 13 libsystem_pthread.dylib 0x00000001ec0618c0 start_wqthread + 8 (:-1) Can anyone provide insights into what might be causing libquic to access an invalid address in this context? Any help or suggestions for further diagnostics would be greatly appreciated.
3
2
805
1d
macOS 27.0/27.0.1: nstat_idle_check vs. flow-divert UDP socket close lock-order deadlock causes configd watchdog panic
Two field incidents show an _nstat_rwlock/UDP socket mutex ABBA deadlock followed by a configd watchdog panic. Raw wait-owner records and UUID-matched KDK stacks identify the same cycle in each incident's earlier configd diagnostic and later panic. Affected systems Both kernels: xnu-13432.1.9~1. Kernel UUIDs are in the attachments. Report AReport BmacOS27.0 (26A428)27.0.1 (26A434)HardwareMac16,10 / T8132Mac15,6 / T6030CloudShell PID87443906Blocked configd threadPID 341, TID 19794593PID 113, TID 309370Our NEDNSProxyProvider can capture CloudShell's DNS UDP traffic and forward it via a separate upstream socket. Provider flows and CloudShell sockets have separate lifetimes. Symptoms and reproduction During DNS UDP forwarding and flow/socket cleanup, CloudShell blocks in final socket close. configd's IPConfigurationAgentQueue blocks creating a UDP socket: a diagnostic is captured after 60 seconds without check-ins, followed by panic/restart after 180 seconds: userspace watchdog timeout: no successful checkins from configd (1 induced crashes) in 180 secondsExpected: socket close completes or returns an error. We have no deterministic reproducer, measured failure rate, or reconstructed per-flow close history. Direct lock-owner evidence Raw waitInfo records (mutex tokens are opaque): Report A, 26A428: thread 18691491: kernel mutex 0x61c831d7f38edbc3 owned by thread 19491850 thread 19491850: krwlock 0xfffffe000cb41730 for writing owned by thread 18691491 thread 19794593: krwlock 0xfffffe000cb41730 for writing owned by thread 18691491Report B, 26A434: thread 36984: kernel mutex 0x44aa649ef3095ae1 owned by thread 12555 thread 12555: krwlock 0xfffffe000ca01730 for writing owned by thread 36984 thread 309370: krwlock 0xfffffe000ca01730 for writing owned by thread 36984In each incident, the first two records form a cycle in both snapshots; the third is configd. All three threads are TH_WAIT / TH_UNINT. UUID-matched kernel stacks Caller-to-callee chains; listed offsets match in both kernels. Kernel worker (A: TID 18691491; B: TID 36984): nstat_idle_check + 808 -> in_pcb_checkstate + 128 -> udp_lock + 200 -> lck_mtx_lock_contended + 1212Holds _nstat_rwlock exclusively; waits for CloudShell's UDP socket mutex. CloudShell (A: TID 19491850; B: TID 12555): soclose + 212 -> soclose_locked + 2300 -> sofreelastref + 60 -> flow_divert_detach + 332 -> in_pcbdetach + 168 -> nstat_pcb_detach + 92 -> lck_rw_lock_exclusive + 180 -> lck_rw_lock_exclusive_gen + 912Holds the socket mutex; waits for _nstat_rwlock. XNU acquires these locks, not application code. configd (downstream victim): socket_common -> socreate_internal -> udp_attach -> nstat_udp_new_pcb -> lck_rw_lock_exclusivePossible enabling condition Matched 27 KDK disassembly shows a provider CLOSE path through cfil_sock_is_dead that directly marks the PCB DEAD and schedules GC without nstat cleanup. Public xnu-12377.121.6 (26.5) uses ordinary detach first. Provider CLOSE and final fd close are distinct. A DEAD PCB can retain an nstat locus/reference. Under _nstat_rwlock, nstat_idle_check removes the locus and releases the reference via in_pcb_checkstate(..., WNT_RELEASE, FALSE), which can take the socket mutex. Final close takes the reverse order; nstat_pcb_detach requests the nstat lock even when the locus is null. This is a possible enabling path from disassembly/source analysis, not proof of the preceding provider CLOSE in either incident. The older source already has opposite static lock orders; 26.5 is not proven immune. Questions for Apple Our provider calls closeReadWithError:nil and closeWriteWithError:nil when finished, as documented for NEAppProxyFlow. This does not validate the entire asynchronous lifecycle. Is this cycle known, and is there a fixed build? How is reverse locking avoided after the direct DEAD transition? Is there a supported workaround preserving flow cleanup? Close rate-limiting is not proven to eliminate the issue. Which diagnostics would help correlate provider CLOSE, DEAD, and final fd close? Attachments and timestamps Four EXCERPT attachments contain watchdog messages, raw frames, KDK annotations, and wait-owner records. Full reports are retained for private Feedback Assistant submission. 26A428_panic_excerpt.txt 26A428_configd_excerpt.txt 26A434_panic_excerpt.txt 26A434_configd_excerpt.txt 2026-10-15, +0800: configd capture -> embedded panic event: A: 04:24:43.1258 -> 04:26:44 B: 08:49:29.4349 -> 08:51:32 Use embedded panic times, not file/header timestamps. Original pairs match on boot identity; identifiers are omitted from excerpts. Sources: NEAppProxyFlow, XNU 26.5 comparison.
2
0
94
1d
Can iOS apps use NWEthernetChannel for LLDP over USB Ethernet?
I have a free Apple Developer account and am evaluating an iPhone app that listens for LLDP frames from a USB Ethernet adapter using NWEthernetChannel. Apple’s documentation says this API requires com.apple.developer.networking.custom-protocol. Can this entitlement be used for development on a physical iPhone with free provisioning, or do I need paid Apple Developer Program membership? Does the entitlement require Apple’s approval or a capability request? Can an app with this entitlement be distributed through the App Store? I’m targeting iOS 25 and later. I can provide the adapter and iPhone models if useful.
1
0
290
2d
Wi-Fi Aware on macOS: Developer ID provisioning and a supported path for Android interoperability?
I'm developing Androfest, a native Android app with a SwiftUI macOS companion. It provides user-authorized notification forwarding, automatic clipboard synchronization, file transfer, and phone-call controls, with experimental call-audio relay. I need a secure, direct connection between a nearby Android phone and a Mac without requiring both devices to join the same Wi-Fi network or using a cloud relay. Discovery, authenticated pairing, and a bidirectional IP data path are the requirements; application traffic uses certificate-pinned TLS. The Android side uses WifiAwareManager. My development devices are a Galaxy S24 FE running Android 16 / One UI 8 and an Apple silicon M4 Pro Mac running macOS 27.2. I read the DTS answer in “Do Mac computers support Wi-Fi Aware?” (https://developer.apple.com/forums/thread/827887), which explains that the presence of the framework for Mac Catalyst does not imply usable Wi-Fi Aware support on Mac. I am asking specifically about the provisioning information below and whether there is an approved path for this use case. In Certificates, Identifiers & Profiles → Register an App ID → Capabilities → Wi-Fi Aware, the information popover currently shows: Platform Support: iOS Provisioning Support: Development, Ad hoc, App Store Connect, Developer ID Entitlement Keys: com.apple.developer.wifi-aware This is confusing because Developer ID is listed while macOS is not listed under Platform Support. The native macOS SDK declarations I inspected mark the public Wi-Fi Aware APIs unavailable on macOS. Two previously generated Developer ID provisioning profiles for my test App ID had Platform = OSX, but neither contained com.apple.developer.wifi-aware. I also could not find a Wi-Fi Aware-specific approval request in the portal's Capability Requests list. Could Apple clarify the following? Does the Developer ID entry describe a usable configuration for native macOS or Mac Catalyst applications, or is it a portal metadata issue? Is there a supported API, managed entitlement, or approval process for Wi-Fi Aware discovery and IP data paths between a third-party macOS app and an Android device? If so, which target platform, capability, provisioning-profile type, and signing configuration are required? Can that configuration operate on a standard Mac with SIP enabled, default NVRAM/boot arguments, and normal code-signing enforcement? A distributable application needs to work under those conditions. If Wi-Fi Aware is not supported for this scenario, what supported transport would Apple recommend for router-independent Android–Mac communication? Where should I file an enhancement request, and should the portal's Developer ID entry be reported separately? I would appreciate guidance on the supported integration route and any missing provisioning or entitlement step. Thank you.
2
0
101
2d
Captive Network Assistant not presented after joining Wi-Fi via Camera QR code until Settings > Wi-Fi is opened
Steps to reproduce: Open captive network (DHCP + DNS → portal, hotspot-detect.html returns 302). Scan the Wi-Fi QR code with Camera and tap "Join". Stay in Camera. Expected: CNA is presented after a Camera-initiated join, as it is after a join from Settings > Wi-Fi. Actual: CNA is not presented until the user manually opens Settings > Wi-Fi. Impact: In-flight Wi-Fi with no internet, where the portal is mandatory. QR codes are the only onboarding method, and most users stay in Camera. Environment: iOS 18.7, iPhone 15 Pro max Questions: Is there any supported way to have the CNA presented after a Camera-initiated Wi-Fi join, without requiring the user to open Settings or Safari? If not, is this expected behavior, or a bug worth tracking?
5
0
141
6d
Ventura: NWConnection returns server error in waiting state instead of failed state
In our macOS SystemExtension, we are using Network.framework for creating web socket connection to our remote WSS server. We provide authentication token in cookies of the connection and websocket server validates it before upgrading the connection from HTTP to WebSocket. If the cookie is invalid, server returns 403 HTTP status code and closes the connection. When server returns 403, in Big Sur and Monterey, we get state update of failed(let error) where error is NWError.posix(.ECONNABORTED). However, in Ventura, we are getting state waiting(let error) where error is NWError.posix(.ECONNABORTED). As per documentation, waiting state should be received if there is any network error in establishing the connection and connection goes into waiting for network path change. But in this case, TCP connection to server is established, HTTP headers are received and validated by server and then get rejected. So shouldn't this result into connection failed instead of waiting? This behaviour has only changed on Ventura. On Big Sur and Monterey we still get failed with error.
4
0
1.5k
1w
DNSSEC validation hangs since root KSK rollover (KSK-2024, key tag 38696); getaddrinfo never returns
Feedback ID: FB25211847 Since 2026-10-11 the root DNSKEY RRset is signed only by KSK-2024 (key tag 38696). With DNSSEC required using nw_parameters_set_requires_dnssec_validation(parameters, YES), mDNSResponder fetches the full chain (icann.org A/DNSKEY/DS, org DNSKEY/DS, root DNSKEY) from the local resolver. All records are present and all signatures validate. After the root DNSKEY query concludes, no validation result is logged, no result or error is delivered to the getaddrinfo request, and later requests for the same name attach as duplicates to the stuck questions and also never return. Without DNSSEC required, the connection succeeds immediately. Suspected cause: KSK-2024 missing from the built-in trust anchors. Secondary issue: validation failure is not reported to the client; the request hangs indefinitely.
2
1
795
1w
libquic.dylib crash during QUIC path migration on iOS 26 (quic_migration_probe_path / nw_protocol_data_access_buffer)
libquic.dylib crashes with a null/invalid buffer access in nw_protocol_data_access_buffer during QUIC connection path migration on iOS 26. App code is not in the stack — this is entirely within Apple system libraries. We are seeing a consistent crash on iOS 26 that does not reproduce on iOS 17 or iOS 18. The crash occurs on a background thread ("com.apple.network.connections") with no application code in the crashed thread's stack. The crash trace begins in quic_migration_probe_path and terminates in nw_protocol_data_access_buffer + 180, suggesting a use-after-free or buffer lifetime violation during QUIC connection path migration (e.g., Wi-Fi ↔ Cellular handoff). This crash does not appear to be reproducible on demand — it correlates with network path transitions while QUIC connections are active. Our app uses standard URLSession with default/ephemeral session configurations and does not explicitly enable HTTP/3; iOS 26 is automatically upgrading eligible connections. Crash thread (abbreviated): 0 libquic.dylib quic_conn_send_packet + 144 1 libquic.dylib quic_conn_continue_sending + 424 2 libquic.dylib __quic_conn_send_frames_for_key_state_block_invoke_2 + 1244 3 Network nw_protocol_data_access_buffer + 180 ← crash 4 Network nw_protocol_data_copy_buffer 5 Network nw_endpoint_flow_output_frames 6 libquic.dylib quic_conn_send_frames_for_key_state 7 libquic.dylib quic_conn_send_frames 8 libquic.dylib quic_migration_probe_path + 1464 9 libquic.dylib quic_migration_path_established + 2608 10 libquic.dylib __quic_migration_path_event_block_invoke.21 11 libquic.dylib quic_migration_path_event 12 Network nw_protocol_implementation_connected There is no app code in the crashed thread. This is a regression introduced in iOS 26, where libquic.dylib was separated into its own dynamic library and new path migration probe logic was introduced.
9
0
1.4k
1w
iCloud Private Relay: network blocking set up exactly as documented (NXDOMAIN for mask.icloud.com / mask-h2.icloud.com), but iOS shows no alert
We run a Wi-Fi network with our own captive portal and want iOS clients to be told that Private Relay is not available here, as described in Prepare your network or web server for iCloud Private Relay and WWDC21 session 10096. Our setup: Resolver: Unbound, handed out via DHCP, with local-zone: "mask.icloud.com" always_nxdomain and local-zone: "mask-h2.icloud.com" always_nxdomain. The network is IPv4-only. The captive portal intercepts all UDP port 53 traffic and drops everything else (e.g. port 853). TCP port 53 is not served. We also tried NXDOMAIN for mask-api.icloud.com, apple-native-relay.apple.com, mask.apple-dns.net, mask-api.fe.apple-dns.net and mask-t.apple-dns.net, with no change. [Result of our resolver log: do queries for the mask hosts arrive from the test iPhone, and what do we answer? Before and after captive-portal login?] What the documentation promises: “users will be alerted that they need to either disable Private Relay for that network, or choose another network.” What happens: On [iPhone model], iOS [exact version/build], with Private Relay enabled in iCloud settings and “Limit IP Address Tracking” [on/off] for this Wi-Fi, joining our network shows no message at all. [What the user sees instead: Safari loads normally / pages fail / nothing changes.] Counter-observation: On a hotel guest network (Marriott) the same kind of device did show a notice, so the feature seems to still work on current iOS. We do not know how that network blocks Private Relay. Related reports: thread 687684 and thread 722315 describe a similar gap (network-level NXDOMAIN gives no alert, on-device DNS filtering does). Our own report: FB[number], no response so far. Questions: Which conditions must be met for iOS to show the message? Is NXDOMAIN for the two documented names enough, or does the alert also depend on other hostnames, the DNS response type (NXDOMAIN vs. NODATA), or the state of the network (e.g. before/after captive login)? Is the alert suppressed when “Limit IP Address Tracking” was turned off for that Wi-Fi earlier, or when Private Relay has already connected successfully on another network? Is the documentation still accurate for current iOS releases? If not, could it be updated?
3
0
228
2w
NetworkConnection: DATAGRAM offer 1400 but usableDatagramFrameSize returns 0 (FB25125957)
I am looking for guidance on the new Swift NetworkConnection<QUIC> DATAGRAM API. A Network.framework defect has not been established. I filed FB25125957, which includes a minimal reproducible source package and numeric evidence. Environment Apple Silicon M2, macOS 27.0.1 (26A434), Xcode 27.0 (27A266a), macOS SDK 27.0 (26A425), Swift 6.4. Builds use an explicit DEVELOPER_DIR, Swift 6 strict concurrency, and warnings as errors. Apple-to-Apple localhost case Both endpoints offer maxDatagramFrameSize(1400), explicitly start their parent connections, and wait for ready. Fresh certificate mutual TLS uses a private test root, role-specific SAN validation, and preknown exact leaf pins. No system trust or keychain modification. After ready, the client awaits parent.datagrams, then reads usableDatagramFrameSize on that same parent. The accessor returns and the getter returns 0. Both endpoints passed their current TLS 1.3 / ALPN / certificate gates, with no early data. The server getter remained unobserved when our local guard stopped this case. No DATAGRAM payload was sent. Our capability error 1005 is a local guard, not NWError. Independent-peer case One Apple listener and an aioquic 1.3.0 client, both advertising 1400, with the same current root/SAN/exact-pin authentication requirements. Both passed mutual authentication, TLS 1.3 / 0x1301 / exact ALPN, with no resumption or early data. The peer decoded Apple's remote max_datagram_frame_size=1400 (present) and recorded its own serialized offer=1400. On the same accepted Apple parent, await datagrams returned and usableDatagramFrameSize completed with 0. The peer's local serialization does not prove that Apple parsed its offer. No application DATAGRAM or media payload was sent. Questions Does ready guarantee that the peer DATAGRAM transport parameter has been processed? Does completing await parent.datagrams provide a stronger guarantee? If a zero usable capacity is transient, what supported event or API should be awaited rather than arbitrary sleeps/retries? What is the supported start/ready/datagrams ordering and parent lifetime for outgoing versus accepted connections? Is opening a reliable stream first a prerequisite? Is there a minimal certificate-TLS DATAGRAM example? Which public diagnostic can distinguish an absent/zero peer advertisement from usable-size calculation, interoperability, or lifecycle issues? Is there a known limitation in this exact OS/SDK, a fixed version, or an authentication-preserving workaround? The source and evidence are attached to FB25125957. They contain no private keys, passwords, pairing credentials, user footage, app bundles, or full qlog. I am asking about basic DATAGRAM availability, not claiming any Wi-Fi or video performance result.
0
0
97
2w
SOCKS5 proxies (from PAC or ProxyConfiguration) are never applied to QUIC, and NWConnection silently connects directly
On macOS 27.0.1 (26A434), a SOCKS5 proxy is applied to TCP connections but never to QUIC. This happens whether the proxy comes from a PAC file in the system proxy settings or from an explicit ProxyConfiguration. The system SOCKS5 client only ever sends CONNECT; it never sends UDP ASSOCIATE (RFC 1928, CMD 0x03), so it has no way to carry UDP. URLSession handles this by staying on TCP through the proxy. A QUIC NWConnection instead connects directly to the origin, as if no proxy were configured. Setup. The system proxy settings use a PAC URL (ProxyAutoConfigURLString in scutil --proxy; in my test it was installed through NEProxySettings.proxyAutoConfigurationURL). For the test host, a public site that serves HTTP/3, the PAC returns SOCKS5 127.0.0.1:<port>. For the explicit cases I used a local SOCKS5 server that logs every command it receives. With the system PAC, default parameters, no explicit proxy: URLSession, assumesHTTP3Capable = true: goes through the proxy over TCP (transaction metrics: h2, isProxyConnection == true, remote address 127.0.0.1:<port>). HTTP/3 isn't used, which is consistent: the proxy can't carry it. NWConnection, TLS: currentPath.remoteEndpoint is 127.0.0.1:<port>. The PAC is honored. NWConnection, QUIC (ALPN h3): .ready in about 170 ms, and currentPath.remoteEndpoint is the origin's own address. The PAC is ignored. With an explicit proxy, set via NWParameters.PrivacyContext.proxyConfigurations: QUIC with a working SOCKS5 server: .ready, and the server receives nothing — no CONNECT, no UDP ASSOCIATE. QUIC with the proxy pointing at a port nothing listens on: still .ready, still connected to the origin's own address. TLS with that same dead proxy: stays in .waiting with ECONNREFUSED. The proxy is honored for TCP. At no point did the SOCKS5 server receive a UDP ASSOCIATE request. Cases 3–5 are the concerning part. An app that configures a proxy, or a user or administrator who installs a PAC, reasonably expects traffic to go through the proxy or fail. Instead, QUIC traffic goes straight to the origin, which then sees the device's own address, and nothing in the API signals that the proxy was skipped. Minimal snippet: import Network let host: NWEndpoint.Host = "YOUR_HTTP3_HOST" // any public host that serves HTTP/3 let parameters = NWParameters(quic: NWProtocolQUIC.Options(alpn: ["h3"])) // Remove these four lines to test with the system PAC instead. let privacy = NWParameters.PrivacyContext(description: "socks5-quic") privacy.proxyConfigurations = [ ProxyConfiguration(socksv5Proxy: .hostPort(host: "127.0.0.1", port: 1099)) // nothing listens on 1099 ] parameters.setPrivacyContext(privacy) let connection = NWConnection(host: host, port: 443, using: parameters) connection.stateUpdateHandler = { state in if case .ready = state { // Prints the origin's own address. With NWParameters.tls instead of QUIC, // the same proxy configuration leaves the connection in .waiting (ECONNREFUSED). print(connection.currentPath?.remoteEndpoint as Any) } } connection.start(queue: .main) I filed this as FB24760235 on 09/13/2026 and haven't had a response, so I'd like to ask here: Is it intended that a QUIC NWConnection ignores both the system PAC and proxyConfigurations? If so, how can an app find out that a connection went direct? And shouldn't a connection that can't honor the configured proxy fail rather than bypass it? Is UDP ASSOCIATE support planned for the system SOCKS5 client, so that QUIC and other UDP traffic can follow a SOCKS5 proxy the way TCP does? Thanks!
3
0
786
2w
Can't the input loop of a NWProtocolFramerImplementation be like...
Recently, I've been asking here about creating a NWProtocolFramerImplementation that splits across line breaks. Examples and questions usually involve some glob in a "while true" loop. From reading around, couldn't that function be implemented as: func handleInput(framer: NWProtocolFramer.Instance) -> Int { // Pull out objects until an incomplete object is discovered. var domainData: Data? while framer.parseInput( minimumIncompleteLength: 1, maximumLength: .max, parse: { buffer, isComplete in guard let buffer else { return 0 } // Why is `buffer` an Optional? // Other checks and computations // Mutate domainData with the result of this iteration. // Return how many bytes from the buffer are an irrevocable part of domainData. } ) { defer { domainData = nil } guard let domainData else { break } let domainMessage = //... let framerMessage = NWProtocolFramer.Message(myMessage: domainMessage) // The return value is whether or not the delivery was immediate. _ = framer.deliverInputNoCopy(length: domainData.count, message: framerMessage, isComplete: true) } return 0 } A clear separation of concerns, and makes figuring out what's going on easier.
1
0
350
2w
Unexplained behavioural changes in BSD sockets on macos 26.6.x and 27
We have started seeing some unexplained behavioural differences in the BSD socket APIs on macos 26.6.x and macos 27. On a different front, there also seems to be some changes in the ptrace area for PT_DETACH. Historically, the release notes of macos don't include any mention of such changes. And unfortunately, prior experience of filing feedback assistant issues too hasn't been very motivating due to lack of any response on those tickets. These forums have been very helpful in trying to understand some of these changes, and I plan to raise separate topics for the issues shortly. In the past, the other useful option I have used is reading up the kernel sources published at https://opensource.apple.com/releases/. But there hasn't been any updates for macos 26.6 or macos 27. It will be very useful if macos 26.6 and 27 releases are published there. That will help expedite investigating some of these failures. BSD socket APIs in the recent releases of macos have seen several issues (feedback assistant issues have been filed). The implications of these failures is that we (in OpenJDK) have had to disable several tests on recent versions of macos which has reduced the test coverage and reliability of these very primitive networking APIs on this platform.
5
0
371
3w
Dual-stack UDP socket bound to port 0 can get a port already in use on 127.0.0.1 (FB25058707)
I filed FB25058707 and wanted to make sure it reaches the right people, because it causes silent datagram loss that's hard to trace. On macOS, when a dual-stack UDP socket (AF_INET6 with IPV6_V6ONLY=0) binds [::]:0, the kernel sometimes gives it a port that an AF_INET socket already has bound to 127.0.0.1. Datagrams sent to 127.0.0.1 on that port then go to the AF_INET socket, so the dual-stack socket never receives them. An explicit bind of the same socket to that port fails with EADDRINUSE; only port-0 assignment hands it out. This program binds 1000 AF_INET sockets to 127.0.0.1:0, then binds dual-stack sockets to [::]:0 and checks where a datagram to 127.0.0.1:port lands whenever the port is already held. It's loopback only and runs in a few seconds: /* * A dual-stack UDP socket (AF_INET6, IPV6_V6ONLY=0) bound to [::]:0 can be * assigned a port already bound by an AF_INET socket on 127.0.0.1. Datagrams * to 127.0.0.1:port then reach the AF_INET socket, not the dual-stack one. * Loopback only. Build: cc -O2 -o dualstack_port0 dualstack_port0.c */ #include <arpa/inet.h> #include <errno.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/resource.h> #include <sys/socket.h> #include <sys/time.h> #include <unistd.h> #define HELD 1000 #define TRIALS 2000 static int held[65536]; static void die(const char *what) { perror(what); exit(1); } static int port_of(int s) { struct sockaddr_storage ss; socklen_t len = sizeof(ss); if (getsockname(s, (struct sockaddr *)&ss, &len) != 0) die("getsockname"); if (ss.ss_family == AF_INET) return ntohs(((struct sockaddr_in *)&ss)->sin_port); return ntohs(((struct sockaddr_in6 *)&ss)->sin6_port); } static int bind_v4(in_addr_t addr) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) die("socket AF_INET"); struct sockaddr_in sin = { .sin_len = sizeof(sin), .sin_family = AF_INET, .sin_addr.s_addr = addr }; if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) != 0) die("bind AF_INET"); return s; } static int bind_dual(void) { int s = socket(AF_INET6, SOCK_DGRAM, 0), off = 0; if (s < 0) die("socket AF_INET6"); if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)) != 0) die("IPV6_V6ONLY"); struct sockaddr_in6 sin6 = { .sin6_len = sizeof(sin6), .sin6_family = AF_INET6, .sin6_addr = in6addr_any }; if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) die("bind AF_INET6"); return s; } /* Returns 1 if s receives token within 100ms, skipping other datagrams. */ static int got(int s, const char *token) { struct timeval tv = { .tv_sec = 0, .tv_usec = 100000 }; if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) die("SO_RCVTIMEO"); char buf[64]; for (;;) { ssize_t n = recv(s, buf, sizeof(buf) - 1, 0); if (n < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; die("recv"); } buf[n] = 0; if (strcmp(buf, token) == 0) return 1; } } int main(void) { struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != 0) die("getrlimit"); rl.rlim_cur = rl.rlim_max < 4096 ? rl.rlim_max : 4096; if (setrlimit(RLIMIT_NOFILE, &rl) != 0) die("setrlimit"); memset(held, -1, sizeof(held)); for (int i = 0; i < HELD;) { int s = bind_v4(htonl(INADDR_LOOPBACK)), p = port_of(s); if (held[p] >= 0) { close(s); continue; } held[p] = s; i++; } int sender = bind_v4(htonl(INADDR_LOOPBACK)); for (int dual = 1; dual >= 0; dual--) { int collisions = 0, to_held = 0, to_wild = 0; for (int i = 0; i < TRIALS; i++) { int w = dual ? bind_dual() : bind_v4(htonl(INADDR_ANY)), p = port_of(w); if (held[p] >= 0) { collisions++; char token[32]; snprintf(token, sizeof(token), "%d-%d", dual, i); struct sockaddr_in to = { .sin_len = sizeof(to), .sin_family = AF_INET, .sin_port = htons(p), .sin_addr.s_addr = htonl(INADDR_LOOPBACK) }; if (sendto(sender, token, strlen(token), 0, (struct sockaddr *)&to, sizeof(to)) < 0) die("sendto"); to_held += got(held[p], token); to_wild += got(w, token); } close(w); } printf("%s: %d/%d wildcard binds got a port held by a 127.0.0.1 socket; the probe reached that socket %d times, the wildcard socket %d times\n", dual ? "AF_INET6 V6ONLY=0 [::]:0" : "AF_INET 0.0.0.0:0 ", collisions, TRIALS, to_held, to_wild); } return 0; } On macOS 27.0 (26A428): AF_INET6 V6ONLY=0 [::]:0: 122/2000 wildcard binds got a port held by a 127.0.0.1 socket; the probe reached that socket 122 times, the wildcard socket 0 times AF_INET 0.0.0.0:0 : 0/2000 wildcard binds got a port held by a 127.0.0.1 socket; the probe reached that socket 0 times, the wildcard socket 0 times macOS 15.8.1 x86_64 gives the same pattern, and a Go version of the test reproduces on 15.7.9 and 26.6.2 as well. FreeBSD 15.1 gives 0/2000 with a dual-stack socket. From the public XNU source (xnu-12377.121.6), in6_pcbsetport checks candidate ports with in6_pcblookup_local, which skips PCBs without INP_IPV6, while in6_pcbbind does an IPv4 PCB lookup for dual-stack wildcard binds. That would explain why an explicit bind is refused but port 0 isn't. This is the cause of the long-standing Go issue golang/go#67226, since Go's net.ListenUDP("udp", 0.0.0.0:0) creates exactly this socket, and of intermittent failures in QUIC client tests. Using an AF_INET socket for IPv4-only traffic avoids it, but there's no workaround for a socket that needs both families on one port.
2
0
305
3w
Networking Resources
General: Forums subtopic: App & System Services > Networking TN3151 Choosing the right networking API Networking Overview document — Despite the fact that this is in the archive, this is still really useful. TLS for App Developers forums post Choosing a Network Debugging Tool documentation WWDC 2019 Session 712 Advances in Networking, Part 1 — This explains the concept of constrained networking, which is Apple’s preferred solution to questions like How do I check whether I’m on Wi-Fi? TN3135 Low-level networking on watchOS TN3179 Understanding local network privacy Adapt to changing network conditions tech talk TCP and UDP ports used by Apple software products support article Understanding Also-Ran Connections forums post Extra-ordinary Networking forums post Foundation networking: Forums tags: Foundation, CFNetwork URL Loading System documentation — NSURLSession, or URLSession in Swift, is the recommended API for HTTP[S] on Apple platforms. Moving to Fewer, Larger Transfers forums post Testing Background Session Code forums post Network framework: Forums tag: Network Network framework documentation — Network framework is the recommended API for TCP, UDP, and QUIC on Apple platforms. WWDC 2025 Session 250 Use structured concurrency with Network framework — This is a great introduction to the new Network framework API introduced in appleOS 2026. Building a custom peer-to-peer protocol sample code (aka TicTacToe) Implementing netcat with Network Framework sample code (aka nwcat) Configuring a Wi-Fi accessory to join a network sample code Moving from Multipeer Connectivity to Network Framework forums post NWEndpoint History and Advice forums post Wi-Fi (general): How to modernize your captive network developer news post Wi-Fi Fundamentals forums post Filing a Wi-Fi Bug Report forums post Working with a Wi-Fi Accessory forums post — This is part of the Extra-ordinary Networking series. Wi-Fi (iOS): TN3111 iOS Wi-Fi API overview technote Wi-Fi Aware framework documentation Building peer-to-peer apps sample code WirelessInsights framework documentation iOS Network Signal Strength forums post Network Extension Resources Wi-Fi on macOS: Forums tag: Core WLAN Core WLAN framework documentation Secure networking: Forums tags: Security Apple Platform Security support document Preventing Insecure Network Connections documentation — This is all about App Transport Security (ATS). WWDC 2017 Session 701 Your Apps and Evolving Network Security Standards [1] — This is generally interesting, but the section starting at 17:40 is, AFAIK, the best information from Apple about how certificate revocation works on modern systems. WWDC 2025 Session 314 Get ahead with quantum-secure cryptography Available trusted root certificates for Apple operating systems support article Requirements for trusted certificates in iOS 13 and macOS 10.15 support article About upcoming limits on trusted certificates support article Apple’s Certificate Transparency policy support article What’s new for enterprise in iOS 18 support article — This discusses new key usage requirements. Prepare your network environment for stricter security requirements support article — This is primarily of interest to folks developing management software, for example, an MDM server. Technote 2232 HTTPS Server Trust Evaluation Technote 2326 Creating Certificates for TLS Testing QA1948 HTTPS and Test Servers Miscellaneous: More network-related forums tags: 5G, QUIC, Bonjour On FTP forums post Using the Multicast Networking Additional Capability forums post Investigating Network Latency Problems forums post Share and Enjoy — Quinn “The Eskimo!” @ Developer Technical Support @ Apple let myEmail = "eskimo" + "1" + "@" + "apple.com" [1] This video is no longer available from Apple, but the URL should help you locate other sources of this info.
Replies
0
Boosts
0
Views
6.7k
Activity
Sep ’26
tvOS 14.6 fails trying to bind UDP/1900 (succeeds on tvOS Simulator)
I'm working on a tvOS app/library which uses SSDP. The code binds a UDP socket to port 1900 and joins a multicast group using BSD Socket library. I believe this used to work on a previous tvOS version, but specifically as of tvOS 14.6 there is a divergence between the tvOS Simulator and actual devices. Actual devices fail at the call to bind, with an Address Already in Use error. The tvOS simulator binds just fine after throwing up a macOS firewall 'allow incoming connections' dialog. Any ideas what the official situation is here? The com.apple.developer.networking.multicast entitlement seems to be iOS only at this time, is that correct? Also there seems to be no 'local networking' permission dialog in tvOS - is that also correct?
Replies
5
Boosts
0
Views
1.4k
Activity
15h
NWConnection/NWListener: .failed, .cancelled, and State Handler Semantics
Hi, I am using Apple's Network.framework APIs, specifically NWConnection and NWListener over UDP, in my project. Both APIs use state-update handlers, and I would like to clarify a few aspects of their lifecycle and callback-delivery semantics. Are .failed and .cancelled delivered only once during the lifecycle? For a given NWConnection or NWListener instance, my understanding is that .failed is a terminal state and the object does not recover from it. Is .failed guaranteed to be delivered at most once during the lifetime of an instance? If the object enters .failed, can I call cancel() on that same instance? If so, will the state-update handler subsequently receive .cancelled? Is .cancelled guaranteed to be delivered at most once per instance, even if cancel() is called multiple times? If the object is in the .waiting state and I call cancel(), causing it to transition to .cancelled, is it guaranteed that .failed will not subsequently be delivered for that instance? Since there is no transition from .cancelled -> .failed? What happens when stateUpdateHandler is replaced while callbacks are pending? Consider the following scenario: I create an NWConnection and assign a stateUpdateHandler (Handler A). The connection's state changes, and a callback for Handler A is enqueued on the connection's associated DispatchQueue. Before that queued callback executes, I replace stateUpdateHandler with Handler B. If the callback for Handler A was already enqueued before the handler was replaced, will Handler A still be invoked? My understanding is Yes, since it was already queued. If the object is in the .waiting state and I call cancel(), causing it to transition to .cancelled, is it guaranteed that .failed will not subsequently be delivered for that instance Can the same state notification be delivered to both Handler A and Handler B, or is it delivered to only one of them i.e., Handler A, since during the state-change Handler A was the one as stateUpdateHandler? Thanks!
Replies
3
Boosts
1
Views
338
Activity
1d
Is Data.withUnsafeMutableBytes(_:) safe to rely on long-term for C++ interop?
Hi, in our project we use Network.framework, with NWListener and NWConnection, to send and receive data over UDP. We use the receiveMessage(completion:) API, which delivers the data as a Data object. We assign this Data to a property of one of our classes, which we continue to work with after the completion handler returns. We pass that property's bytes to our C++ code (through Swift–C++ interop) using Data.withUnsafeMutableBytes(_:). The C++ code modifies the bytes in place, and we then continue working with the modified Data in Swift. Since nothing else refers to the Data we received, the bytes are modified in place and no copy is made. However, if another Data value shared the same buffer, just calling withUnsafeMutableBytes(_:) would create a copy, whether or not we actually modified the bytes. Our understanding is that this is because the method is declared mutating, so the call itself is treated as a write and copy-on-write makes the buffer unique before the pointer is handed out. We also understand that ResultType : ~Copyable (along with the typed throws(E)) only relaxes the requirements on the closure's return type and doesn't affect this behaviour. We understand this approach is unsafe and that the pointer "should not be stored and used outside of the lifetime of the call to the closure", so we will take care of that. Since the typed-pointer variants of APIs such as Data.withUnsafeBytes were deprecated in Swift 5, we would also like to confirm that withUnsafeMutableBytes(_:), the variant that takes an UnsafeMutableRawBufferPointer, is the intended long-term API and isn't expected to be deprecated in the same way.
Replies
5
Boosts
1
Views
208
Activity
1d
Crash in libquic.dylib | quic_recovery_pto | iOS 26.1
Hello, I am investigating a recurring crash that appears to be originating within the system's network stack. OS Version: iPhone OS 26.1 (23B85) Role: Foreground Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000 Exception Codes: 0x0000000000000001, 0x0000000000000000 Triggered by Thread: 19 Description: The crash is triggered by Thread 19 and occurs deep within libquic.dylib during a QUIC recovery timer event. Based on the backtrace, the failure happens in quic_recovery_pto. The issue seems to occur when a protocol instance schedules a wakeup, leading to a null pointer dereference in the system library. Crashed Thread Backtrace snippet:Thread 19 Crashed: Thread 19 Crashed: 0 libquic.dylib 0x00000001a00a38cc quic_recovery_pto + 72 (quic_recovery.c:1259) 1 libquic.dylib 0x00000001a00a3390 quic_recovery_timer_fired + 132 (quic_recovery.c:1460) 2 libquic.dylib 0x00000001a00a1f8c quic_timer_run + 248 (quic_timer.c:210) 3 Network 0x000000018ec76cbc __nw_protocol_instance_schedule_wakeup_block_invoke + 76 (protocol_implementation.cpp:5847) 4 Network 0x000000018eba34e0 __nw_context_reset_timer_block_with_time_block_invoke + 268 (context.cpp:2224) 5 libdispatch.dylib 0x00000001c84727ec _dispatch_client_callout + 16 (client_callout.mm:85) 6 libdispatch.dylib 0x00000001c845d664 _dispatch_continuation_pop + 596 (queue.c:349) 7 libdispatch.dylib 0x00000001c8470528 _dispatch_source_latch_and_call + 396 (source.c:601) 8 libdispatch.dylib 0x00000001c846f1fc _dispatch_source_invoke + 844 (source.c:966) 9 libdispatch.dylib 0x00000001c8463288 _dispatch_workloop_invoke + 1612 (queue.c:4761) 10 libdispatch.dylib 0x00000001c846c3ec _dispatch_root_queue_drain_deferred_wlh + 292 (queue.c:7265) 11 libdispatch.dylib 0x00000001c846bce4 _dispatch_workloop_worker_thread + 692 (queue.c:6859) 12 libsystem_pthread.dylib 0x00000001ec0623b8 _pthread_wqthread + 292 (pthread.c:2696) 13 libsystem_pthread.dylib 0x00000001ec0618c0 start_wqthread + 8 (:-1) Can anyone provide insights into what might be causing libquic to access an invalid address in this context? Any help or suggestions for further diagnostics would be greatly appreciated.
Replies
3
Boosts
2
Views
805
Activity
1d
macOS 27.0/27.0.1: nstat_idle_check vs. flow-divert UDP socket close lock-order deadlock causes configd watchdog panic
Two field incidents show an _nstat_rwlock/UDP socket mutex ABBA deadlock followed by a configd watchdog panic. Raw wait-owner records and UUID-matched KDK stacks identify the same cycle in each incident's earlier configd diagnostic and later panic. Affected systems Both kernels: xnu-13432.1.9~1. Kernel UUIDs are in the attachments. Report AReport BmacOS27.0 (26A428)27.0.1 (26A434)HardwareMac16,10 / T8132Mac15,6 / T6030CloudShell PID87443906Blocked configd threadPID 341, TID 19794593PID 113, TID 309370Our NEDNSProxyProvider can capture CloudShell's DNS UDP traffic and forward it via a separate upstream socket. Provider flows and CloudShell sockets have separate lifetimes. Symptoms and reproduction During DNS UDP forwarding and flow/socket cleanup, CloudShell blocks in final socket close. configd's IPConfigurationAgentQueue blocks creating a UDP socket: a diagnostic is captured after 60 seconds without check-ins, followed by panic/restart after 180 seconds: userspace watchdog timeout: no successful checkins from configd (1 induced crashes) in 180 secondsExpected: socket close completes or returns an error. We have no deterministic reproducer, measured failure rate, or reconstructed per-flow close history. Direct lock-owner evidence Raw waitInfo records (mutex tokens are opaque): Report A, 26A428: thread 18691491: kernel mutex 0x61c831d7f38edbc3 owned by thread 19491850 thread 19491850: krwlock 0xfffffe000cb41730 for writing owned by thread 18691491 thread 19794593: krwlock 0xfffffe000cb41730 for writing owned by thread 18691491Report B, 26A434: thread 36984: kernel mutex 0x44aa649ef3095ae1 owned by thread 12555 thread 12555: krwlock 0xfffffe000ca01730 for writing owned by thread 36984 thread 309370: krwlock 0xfffffe000ca01730 for writing owned by thread 36984In each incident, the first two records form a cycle in both snapshots; the third is configd. All three threads are TH_WAIT / TH_UNINT. UUID-matched kernel stacks Caller-to-callee chains; listed offsets match in both kernels. Kernel worker (A: TID 18691491; B: TID 36984): nstat_idle_check + 808 -> in_pcb_checkstate + 128 -> udp_lock + 200 -> lck_mtx_lock_contended + 1212Holds _nstat_rwlock exclusively; waits for CloudShell's UDP socket mutex. CloudShell (A: TID 19491850; B: TID 12555): soclose + 212 -> soclose_locked + 2300 -> sofreelastref + 60 -> flow_divert_detach + 332 -> in_pcbdetach + 168 -> nstat_pcb_detach + 92 -> lck_rw_lock_exclusive + 180 -> lck_rw_lock_exclusive_gen + 912Holds the socket mutex; waits for _nstat_rwlock. XNU acquires these locks, not application code. configd (downstream victim): socket_common -> socreate_internal -> udp_attach -> nstat_udp_new_pcb -> lck_rw_lock_exclusivePossible enabling condition Matched 27 KDK disassembly shows a provider CLOSE path through cfil_sock_is_dead that directly marks the PCB DEAD and schedules GC without nstat cleanup. Public xnu-12377.121.6 (26.5) uses ordinary detach first. Provider CLOSE and final fd close are distinct. A DEAD PCB can retain an nstat locus/reference. Under _nstat_rwlock, nstat_idle_check removes the locus and releases the reference via in_pcb_checkstate(..., WNT_RELEASE, FALSE), which can take the socket mutex. Final close takes the reverse order; nstat_pcb_detach requests the nstat lock even when the locus is null. This is a possible enabling path from disassembly/source analysis, not proof of the preceding provider CLOSE in either incident. The older source already has opposite static lock orders; 26.5 is not proven immune. Questions for Apple Our provider calls closeReadWithError:nil and closeWriteWithError:nil when finished, as documented for NEAppProxyFlow. This does not validate the entire asynchronous lifecycle. Is this cycle known, and is there a fixed build? How is reverse locking avoided after the direct DEAD transition? Is there a supported workaround preserving flow cleanup? Close rate-limiting is not proven to eliminate the issue. Which diagnostics would help correlate provider CLOSE, DEAD, and final fd close? Attachments and timestamps Four EXCERPT attachments contain watchdog messages, raw frames, KDK annotations, and wait-owner records. Full reports are retained for private Feedback Assistant submission. 26A428_panic_excerpt.txt 26A428_configd_excerpt.txt 26A434_panic_excerpt.txt 26A434_configd_excerpt.txt 2026-10-15, +0800: configd capture -> embedded panic event: A: 04:24:43.1258 -> 04:26:44 B: 08:49:29.4349 -> 08:51:32 Use embedded panic times, not file/header timestamps. Original pairs match on boot identity; identifiers are omitted from excerpts. Sources: NEAppProxyFlow, XNU 26.5 comparison.
Replies
2
Boosts
0
Views
94
Activity
1d
Can iOS apps use NWEthernetChannel for LLDP over USB Ethernet?
I have a free Apple Developer account and am evaluating an iPhone app that listens for LLDP frames from a USB Ethernet adapter using NWEthernetChannel. Apple’s documentation says this API requires com.apple.developer.networking.custom-protocol. Can this entitlement be used for development on a physical iPhone with free provisioning, or do I need paid Apple Developer Program membership? Does the entitlement require Apple’s approval or a capability request? Can an app with this entitlement be distributed through the App Store? I’m targeting iOS 25 and later. I can provide the adapter and iPhone models if useful.
Replies
1
Boosts
0
Views
290
Activity
2d
Wi-Fi Aware on macOS: Developer ID provisioning and a supported path for Android interoperability?
I'm developing Androfest, a native Android app with a SwiftUI macOS companion. It provides user-authorized notification forwarding, automatic clipboard synchronization, file transfer, and phone-call controls, with experimental call-audio relay. I need a secure, direct connection between a nearby Android phone and a Mac without requiring both devices to join the same Wi-Fi network or using a cloud relay. Discovery, authenticated pairing, and a bidirectional IP data path are the requirements; application traffic uses certificate-pinned TLS. The Android side uses WifiAwareManager. My development devices are a Galaxy S24 FE running Android 16 / One UI 8 and an Apple silicon M4 Pro Mac running macOS 27.2. I read the DTS answer in “Do Mac computers support Wi-Fi Aware?” (https://developer.apple.com/forums/thread/827887), which explains that the presence of the framework for Mac Catalyst does not imply usable Wi-Fi Aware support on Mac. I am asking specifically about the provisioning information below and whether there is an approved path for this use case. In Certificates, Identifiers & Profiles → Register an App ID → Capabilities → Wi-Fi Aware, the information popover currently shows: Platform Support: iOS Provisioning Support: Development, Ad hoc, App Store Connect, Developer ID Entitlement Keys: com.apple.developer.wifi-aware This is confusing because Developer ID is listed while macOS is not listed under Platform Support. The native macOS SDK declarations I inspected mark the public Wi-Fi Aware APIs unavailable on macOS. Two previously generated Developer ID provisioning profiles for my test App ID had Platform = OSX, but neither contained com.apple.developer.wifi-aware. I also could not find a Wi-Fi Aware-specific approval request in the portal's Capability Requests list. Could Apple clarify the following? Does the Developer ID entry describe a usable configuration for native macOS or Mac Catalyst applications, or is it a portal metadata issue? Is there a supported API, managed entitlement, or approval process for Wi-Fi Aware discovery and IP data paths between a third-party macOS app and an Android device? If so, which target platform, capability, provisioning-profile type, and signing configuration are required? Can that configuration operate on a standard Mac with SIP enabled, default NVRAM/boot arguments, and normal code-signing enforcement? A distributable application needs to work under those conditions. If Wi-Fi Aware is not supported for this scenario, what supported transport would Apple recommend for router-independent Android–Mac communication? Where should I file an enhancement request, and should the portal's Developer ID entry be reported separately? I would appreciate guidance on the supported integration route and any missing provisioning or entitlement step. Thank you.
Replies
2
Boosts
0
Views
101
Activity
2d
Captive Network Assistant not presented after joining Wi-Fi via Camera QR code until Settings > Wi-Fi is opened
Steps to reproduce: Open captive network (DHCP + DNS → portal, hotspot-detect.html returns 302). Scan the Wi-Fi QR code with Camera and tap "Join". Stay in Camera. Expected: CNA is presented after a Camera-initiated join, as it is after a join from Settings > Wi-Fi. Actual: CNA is not presented until the user manually opens Settings > Wi-Fi. Impact: In-flight Wi-Fi with no internet, where the portal is mandatory. QR codes are the only onboarding method, and most users stay in Camera. Environment: iOS 18.7, iPhone 15 Pro max Questions: Is there any supported way to have the CNA presented after a Camera-initiated Wi-Fi join, without requiring the user to open Settings or Safari? If not, is this expected behavior, or a bug worth tracking?
Replies
5
Boosts
0
Views
141
Activity
6d
Ventura: NWConnection returns server error in waiting state instead of failed state
In our macOS SystemExtension, we are using Network.framework for creating web socket connection to our remote WSS server. We provide authentication token in cookies of the connection and websocket server validates it before upgrading the connection from HTTP to WebSocket. If the cookie is invalid, server returns 403 HTTP status code and closes the connection. When server returns 403, in Big Sur and Monterey, we get state update of failed(let error) where error is NWError.posix(.ECONNABORTED). However, in Ventura, we are getting state waiting(let error) where error is NWError.posix(.ECONNABORTED). As per documentation, waiting state should be received if there is any network error in establishing the connection and connection goes into waiting for network path change. But in this case, TCP connection to server is established, HTTP headers are received and validated by server and then get rejected. So shouldn't this result into connection failed instead of waiting? This behaviour has only changed on Ventura. On Big Sur and Monterey we still get failed with error.
Replies
4
Boosts
0
Views
1.5k
Activity
1w
DNSSEC validation hangs since root KSK rollover (KSK-2024, key tag 38696); getaddrinfo never returns
Feedback ID: FB25211847 Since 2026-10-11 the root DNSKEY RRset is signed only by KSK-2024 (key tag 38696). With DNSSEC required using nw_parameters_set_requires_dnssec_validation(parameters, YES), mDNSResponder fetches the full chain (icann.org A/DNSKEY/DS, org DNSKEY/DS, root DNSKEY) from the local resolver. All records are present and all signatures validate. After the root DNSKEY query concludes, no validation result is logged, no result or error is delivered to the getaddrinfo request, and later requests for the same name attach as duplicates to the stuck questions and also never return. Without DNSSEC required, the connection succeeds immediately. Suspected cause: KSK-2024 missing from the built-in trust anchors. Secondary issue: validation failure is not reported to the client; the request hangs indefinitely.
Replies
2
Boosts
1
Views
795
Activity
1w
libquic.dylib crash during QUIC path migration on iOS 26 (quic_migration_probe_path / nw_protocol_data_access_buffer)
libquic.dylib crashes with a null/invalid buffer access in nw_protocol_data_access_buffer during QUIC connection path migration on iOS 26. App code is not in the stack — this is entirely within Apple system libraries. We are seeing a consistent crash on iOS 26 that does not reproduce on iOS 17 or iOS 18. The crash occurs on a background thread ("com.apple.network.connections") with no application code in the crashed thread's stack. The crash trace begins in quic_migration_probe_path and terminates in nw_protocol_data_access_buffer + 180, suggesting a use-after-free or buffer lifetime violation during QUIC connection path migration (e.g., Wi-Fi ↔ Cellular handoff). This crash does not appear to be reproducible on demand — it correlates with network path transitions while QUIC connections are active. Our app uses standard URLSession with default/ephemeral session configurations and does not explicitly enable HTTP/3; iOS 26 is automatically upgrading eligible connections. Crash thread (abbreviated): 0 libquic.dylib quic_conn_send_packet + 144 1 libquic.dylib quic_conn_continue_sending + 424 2 libquic.dylib __quic_conn_send_frames_for_key_state_block_invoke_2 + 1244 3 Network nw_protocol_data_access_buffer + 180 ← crash 4 Network nw_protocol_data_copy_buffer 5 Network nw_endpoint_flow_output_frames 6 libquic.dylib quic_conn_send_frames_for_key_state 7 libquic.dylib quic_conn_send_frames 8 libquic.dylib quic_migration_probe_path + 1464 9 libquic.dylib quic_migration_path_established + 2608 10 libquic.dylib __quic_migration_path_event_block_invoke.21 11 libquic.dylib quic_migration_path_event 12 Network nw_protocol_implementation_connected There is no app code in the crashed thread. This is a regression introduced in iOS 26, where libquic.dylib was separated into its own dynamic library and new path migration probe logic was introduced.
Replies
9
Boosts
0
Views
1.4k
Activity
1w
iCloud Private Relay: network blocking set up exactly as documented (NXDOMAIN for mask.icloud.com / mask-h2.icloud.com), but iOS shows no alert
We run a Wi-Fi network with our own captive portal and want iOS clients to be told that Private Relay is not available here, as described in Prepare your network or web server for iCloud Private Relay and WWDC21 session 10096. Our setup: Resolver: Unbound, handed out via DHCP, with local-zone: "mask.icloud.com" always_nxdomain and local-zone: "mask-h2.icloud.com" always_nxdomain. The network is IPv4-only. The captive portal intercepts all UDP port 53 traffic and drops everything else (e.g. port 853). TCP port 53 is not served. We also tried NXDOMAIN for mask-api.icloud.com, apple-native-relay.apple.com, mask.apple-dns.net, mask-api.fe.apple-dns.net and mask-t.apple-dns.net, with no change. [Result of our resolver log: do queries for the mask hosts arrive from the test iPhone, and what do we answer? Before and after captive-portal login?] What the documentation promises: “users will be alerted that they need to either disable Private Relay for that network, or choose another network.” What happens: On [iPhone model], iOS [exact version/build], with Private Relay enabled in iCloud settings and “Limit IP Address Tracking” [on/off] for this Wi-Fi, joining our network shows no message at all. [What the user sees instead: Safari loads normally / pages fail / nothing changes.] Counter-observation: On a hotel guest network (Marriott) the same kind of device did show a notice, so the feature seems to still work on current iOS. We do not know how that network blocks Private Relay. Related reports: thread 687684 and thread 722315 describe a similar gap (network-level NXDOMAIN gives no alert, on-device DNS filtering does). Our own report: FB[number], no response so far. Questions: Which conditions must be met for iOS to show the message? Is NXDOMAIN for the two documented names enough, or does the alert also depend on other hostnames, the DNS response type (NXDOMAIN vs. NODATA), or the state of the network (e.g. before/after captive login)? Is the alert suppressed when “Limit IP Address Tracking” was turned off for that Wi-Fi earlier, or when Private Relay has already connected successfully on another network? Is the documentation still accurate for current iOS releases? If not, could it be updated?
Replies
3
Boosts
0
Views
228
Activity
2w
NetworkConnection: DATAGRAM offer 1400 but usableDatagramFrameSize returns 0 (FB25125957)
I am looking for guidance on the new Swift NetworkConnection<QUIC> DATAGRAM API. A Network.framework defect has not been established. I filed FB25125957, which includes a minimal reproducible source package and numeric evidence. Environment Apple Silicon M2, macOS 27.0.1 (26A434), Xcode 27.0 (27A266a), macOS SDK 27.0 (26A425), Swift 6.4. Builds use an explicit DEVELOPER_DIR, Swift 6 strict concurrency, and warnings as errors. Apple-to-Apple localhost case Both endpoints offer maxDatagramFrameSize(1400), explicitly start their parent connections, and wait for ready. Fresh certificate mutual TLS uses a private test root, role-specific SAN validation, and preknown exact leaf pins. No system trust or keychain modification. After ready, the client awaits parent.datagrams, then reads usableDatagramFrameSize on that same parent. The accessor returns and the getter returns 0. Both endpoints passed their current TLS 1.3 / ALPN / certificate gates, with no early data. The server getter remained unobserved when our local guard stopped this case. No DATAGRAM payload was sent. Our capability error 1005 is a local guard, not NWError. Independent-peer case One Apple listener and an aioquic 1.3.0 client, both advertising 1400, with the same current root/SAN/exact-pin authentication requirements. Both passed mutual authentication, TLS 1.3 / 0x1301 / exact ALPN, with no resumption or early data. The peer decoded Apple's remote max_datagram_frame_size=1400 (present) and recorded its own serialized offer=1400. On the same accepted Apple parent, await datagrams returned and usableDatagramFrameSize completed with 0. The peer's local serialization does not prove that Apple parsed its offer. No application DATAGRAM or media payload was sent. Questions Does ready guarantee that the peer DATAGRAM transport parameter has been processed? Does completing await parent.datagrams provide a stronger guarantee? If a zero usable capacity is transient, what supported event or API should be awaited rather than arbitrary sleeps/retries? What is the supported start/ready/datagrams ordering and parent lifetime for outgoing versus accepted connections? Is opening a reliable stream first a prerequisite? Is there a minimal certificate-TLS DATAGRAM example? Which public diagnostic can distinguish an absent/zero peer advertisement from usable-size calculation, interoperability, or lifecycle issues? Is there a known limitation in this exact OS/SDK, a fixed version, or an authentication-preserving workaround? The source and evidence are attached to FB25125957. They contain no private keys, passwords, pairing credentials, user footage, app bundles, or full qlog. I am asking about basic DATAGRAM availability, not claiming any Wi-Fi or video performance result.
Replies
0
Boosts
0
Views
97
Activity
2w
SOCKS5 proxies (from PAC or ProxyConfiguration) are never applied to QUIC, and NWConnection silently connects directly
On macOS 27.0.1 (26A434), a SOCKS5 proxy is applied to TCP connections but never to QUIC. This happens whether the proxy comes from a PAC file in the system proxy settings or from an explicit ProxyConfiguration. The system SOCKS5 client only ever sends CONNECT; it never sends UDP ASSOCIATE (RFC 1928, CMD 0x03), so it has no way to carry UDP. URLSession handles this by staying on TCP through the proxy. A QUIC NWConnection instead connects directly to the origin, as if no proxy were configured. Setup. The system proxy settings use a PAC URL (ProxyAutoConfigURLString in scutil --proxy; in my test it was installed through NEProxySettings.proxyAutoConfigurationURL). For the test host, a public site that serves HTTP/3, the PAC returns SOCKS5 127.0.0.1:<port>. For the explicit cases I used a local SOCKS5 server that logs every command it receives. With the system PAC, default parameters, no explicit proxy: URLSession, assumesHTTP3Capable = true: goes through the proxy over TCP (transaction metrics: h2, isProxyConnection == true, remote address 127.0.0.1:<port>). HTTP/3 isn't used, which is consistent: the proxy can't carry it. NWConnection, TLS: currentPath.remoteEndpoint is 127.0.0.1:<port>. The PAC is honored. NWConnection, QUIC (ALPN h3): .ready in about 170 ms, and currentPath.remoteEndpoint is the origin's own address. The PAC is ignored. With an explicit proxy, set via NWParameters.PrivacyContext.proxyConfigurations: QUIC with a working SOCKS5 server: .ready, and the server receives nothing — no CONNECT, no UDP ASSOCIATE. QUIC with the proxy pointing at a port nothing listens on: still .ready, still connected to the origin's own address. TLS with that same dead proxy: stays in .waiting with ECONNREFUSED. The proxy is honored for TCP. At no point did the SOCKS5 server receive a UDP ASSOCIATE request. Cases 3–5 are the concerning part. An app that configures a proxy, or a user or administrator who installs a PAC, reasonably expects traffic to go through the proxy or fail. Instead, QUIC traffic goes straight to the origin, which then sees the device's own address, and nothing in the API signals that the proxy was skipped. Minimal snippet: import Network let host: NWEndpoint.Host = "YOUR_HTTP3_HOST" // any public host that serves HTTP/3 let parameters = NWParameters(quic: NWProtocolQUIC.Options(alpn: ["h3"])) // Remove these four lines to test with the system PAC instead. let privacy = NWParameters.PrivacyContext(description: "socks5-quic") privacy.proxyConfigurations = [ ProxyConfiguration(socksv5Proxy: .hostPort(host: "127.0.0.1", port: 1099)) // nothing listens on 1099 ] parameters.setPrivacyContext(privacy) let connection = NWConnection(host: host, port: 443, using: parameters) connection.stateUpdateHandler = { state in if case .ready = state { // Prints the origin's own address. With NWParameters.tls instead of QUIC, // the same proxy configuration leaves the connection in .waiting (ECONNREFUSED). print(connection.currentPath?.remoteEndpoint as Any) } } connection.start(queue: .main) I filed this as FB24760235 on 09/13/2026 and haven't had a response, so I'd like to ask here: Is it intended that a QUIC NWConnection ignores both the system PAC and proxyConfigurations? If so, how can an app find out that a connection went direct? And shouldn't a connection that can't honor the configured proxy fail rather than bypass it? Is UDP ASSOCIATE support planned for the system SOCKS5 client, so that QUIC and other UDP traffic can follow a SOCKS5 proxy the way TCP does? Thanks!
Replies
3
Boosts
0
Views
786
Activity
2w
Can't the input loop of a NWProtocolFramerImplementation be like...
Recently, I've been asking here about creating a NWProtocolFramerImplementation that splits across line breaks. Examples and questions usually involve some glob in a "while true" loop. From reading around, couldn't that function be implemented as: func handleInput(framer: NWProtocolFramer.Instance) -> Int { // Pull out objects until an incomplete object is discovered. var domainData: Data? while framer.parseInput( minimumIncompleteLength: 1, maximumLength: .max, parse: { buffer, isComplete in guard let buffer else { return 0 } // Why is `buffer` an Optional? // Other checks and computations // Mutate domainData with the result of this iteration. // Return how many bytes from the buffer are an irrevocable part of domainData. } ) { defer { domainData = nil } guard let domainData else { break } let domainMessage = //... let framerMessage = NWProtocolFramer.Message(myMessage: domainMessage) // The return value is whether or not the delivery was immediate. _ = framer.deliverInputNoCopy(length: domainData.count, message: framerMessage, isComplete: true) } return 0 } A clear separation of concerns, and makes figuring out what's going on easier.
Replies
1
Boosts
0
Views
350
Activity
2w
Unexplained behavioural changes in BSD sockets on macos 26.6.x and 27
We have started seeing some unexplained behavioural differences in the BSD socket APIs on macos 26.6.x and macos 27. On a different front, there also seems to be some changes in the ptrace area for PT_DETACH. Historically, the release notes of macos don't include any mention of such changes. And unfortunately, prior experience of filing feedback assistant issues too hasn't been very motivating due to lack of any response on those tickets. These forums have been very helpful in trying to understand some of these changes, and I plan to raise separate topics for the issues shortly. In the past, the other useful option I have used is reading up the kernel sources published at https://opensource.apple.com/releases/. But there hasn't been any updates for macos 26.6 or macos 27. It will be very useful if macos 26.6 and 27 releases are published there. That will help expedite investigating some of these failures. BSD socket APIs in the recent releases of macos have seen several issues (feedback assistant issues have been filed). The implications of these failures is that we (in OpenJDK) have had to disable several tests on recent versions of macos which has reduced the test coverage and reliability of these very primitive networking APIs on this platform.
Replies
5
Boosts
0
Views
371
Activity
3w
Dual-stack UDP socket bound to port 0 can get a port already in use on 127.0.0.1 (FB25058707)
I filed FB25058707 and wanted to make sure it reaches the right people, because it causes silent datagram loss that's hard to trace. On macOS, when a dual-stack UDP socket (AF_INET6 with IPV6_V6ONLY=0) binds [::]:0, the kernel sometimes gives it a port that an AF_INET socket already has bound to 127.0.0.1. Datagrams sent to 127.0.0.1 on that port then go to the AF_INET socket, so the dual-stack socket never receives them. An explicit bind of the same socket to that port fails with EADDRINUSE; only port-0 assignment hands it out. This program binds 1000 AF_INET sockets to 127.0.0.1:0, then binds dual-stack sockets to [::]:0 and checks where a datagram to 127.0.0.1:port lands whenever the port is already held. It's loopback only and runs in a few seconds: /* * A dual-stack UDP socket (AF_INET6, IPV6_V6ONLY=0) bound to [::]:0 can be * assigned a port already bound by an AF_INET socket on 127.0.0.1. Datagrams * to 127.0.0.1:port then reach the AF_INET socket, not the dual-stack one. * Loopback only. Build: cc -O2 -o dualstack_port0 dualstack_port0.c */ #include <arpa/inet.h> #include <errno.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/resource.h> #include <sys/socket.h> #include <sys/time.h> #include <unistd.h> #define HELD 1000 #define TRIALS 2000 static int held[65536]; static void die(const char *what) { perror(what); exit(1); } static int port_of(int s) { struct sockaddr_storage ss; socklen_t len = sizeof(ss); if (getsockname(s, (struct sockaddr *)&ss, &len) != 0) die("getsockname"); if (ss.ss_family == AF_INET) return ntohs(((struct sockaddr_in *)&ss)->sin_port); return ntohs(((struct sockaddr_in6 *)&ss)->sin6_port); } static int bind_v4(in_addr_t addr) { int s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) die("socket AF_INET"); struct sockaddr_in sin = { .sin_len = sizeof(sin), .sin_family = AF_INET, .sin_addr.s_addr = addr }; if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) != 0) die("bind AF_INET"); return s; } static int bind_dual(void) { int s = socket(AF_INET6, SOCK_DGRAM, 0), off = 0; if (s < 0) die("socket AF_INET6"); if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)) != 0) die("IPV6_V6ONLY"); struct sockaddr_in6 sin6 = { .sin6_len = sizeof(sin6), .sin6_family = AF_INET6, .sin6_addr = in6addr_any }; if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) != 0) die("bind AF_INET6"); return s; } /* Returns 1 if s receives token within 100ms, skipping other datagrams. */ static int got(int s, const char *token) { struct timeval tv = { .tv_sec = 0, .tv_usec = 100000 }; if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) != 0) die("SO_RCVTIMEO"); char buf[64]; for (;;) { ssize_t n = recv(s, buf, sizeof(buf) - 1, 0); if (n < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; die("recv"); } buf[n] = 0; if (strcmp(buf, token) == 0) return 1; } } int main(void) { struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != 0) die("getrlimit"); rl.rlim_cur = rl.rlim_max < 4096 ? rl.rlim_max : 4096; if (setrlimit(RLIMIT_NOFILE, &rl) != 0) die("setrlimit"); memset(held, -1, sizeof(held)); for (int i = 0; i < HELD;) { int s = bind_v4(htonl(INADDR_LOOPBACK)), p = port_of(s); if (held[p] >= 0) { close(s); continue; } held[p] = s; i++; } int sender = bind_v4(htonl(INADDR_LOOPBACK)); for (int dual = 1; dual >= 0; dual--) { int collisions = 0, to_held = 0, to_wild = 0; for (int i = 0; i < TRIALS; i++) { int w = dual ? bind_dual() : bind_v4(htonl(INADDR_ANY)), p = port_of(w); if (held[p] >= 0) { collisions++; char token[32]; snprintf(token, sizeof(token), "%d-%d", dual, i); struct sockaddr_in to = { .sin_len = sizeof(to), .sin_family = AF_INET, .sin_port = htons(p), .sin_addr.s_addr = htonl(INADDR_LOOPBACK) }; if (sendto(sender, token, strlen(token), 0, (struct sockaddr *)&to, sizeof(to)) < 0) die("sendto"); to_held += got(held[p], token); to_wild += got(w, token); } close(w); } printf("%s: %d/%d wildcard binds got a port held by a 127.0.0.1 socket; the probe reached that socket %d times, the wildcard socket %d times\n", dual ? "AF_INET6 V6ONLY=0 [::]:0" : "AF_INET 0.0.0.0:0 ", collisions, TRIALS, to_held, to_wild); } return 0; } On macOS 27.0 (26A428): AF_INET6 V6ONLY=0 [::]:0: 122/2000 wildcard binds got a port held by a 127.0.0.1 socket; the probe reached that socket 122 times, the wildcard socket 0 times AF_INET 0.0.0.0:0 : 0/2000 wildcard binds got a port held by a 127.0.0.1 socket; the probe reached that socket 0 times, the wildcard socket 0 times macOS 15.8.1 x86_64 gives the same pattern, and a Go version of the test reproduces on 15.7.9 and 26.6.2 as well. FreeBSD 15.1 gives 0/2000 with a dual-stack socket. From the public XNU source (xnu-12377.121.6), in6_pcbsetport checks candidate ports with in6_pcblookup_local, which skips PCBs without INP_IPV6, while in6_pcbbind does an IPv4 PCB lookup for dual-stack wildcard binds. That would explain why an explicit bind is refused but port 0 isn't. This is the cause of the long-standing Go issue golang/go#67226, since Go's net.ListenUDP("udp", 0.0.0.0:0) creates exactly this socket, and of intermittent failures in QUIC client tests. Using an AF_INET socket for IPv4-only traffic avoids it, but there's no workaround for a socket that needs both families on one port.
Replies
2
Boosts
0
Views
305
Activity
3w