Skip to content

connect_timeout returns Ok(()) on macOS but socket is not connected #652

@BiagioFesta

Description

@BiagioFesta

I am observing a case on macOS where connect_timeout returns Ok(()) but the socket is not actually connected: peer_addr() (getpeername) immediately returns ENOTCONN.

Minimal reproducer:

use socket2::{Domain, Protocol, SockAddr, Socket, Type};                                                                                                                                                                                                                
use std::{net::SocketAddr, time::Duration};                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                          
fn main() {                                                                                                                                                                                                                                                             
    let remote_addr = "[<remote-ipv6>]:443"                                                                                                                                                                                                                 
        .parse::<SocketAddr>()                                                                                                                                                                                                                                          
        .unwrap();                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                          
    let socket = Socket::new(Domain::IPV6, Type::STREAM, Some(Protocol::TCP)).unwrap();                                                                                                                                                                                 
                                                                                                                                                                                                                                                                          
    socket                                                                                                                                                                                                                                                              
        .connect_timeout(&SockAddr::from(remote_addr), Duration::from_secs(10))                                                                                                                                                                                         
        .unwrap();                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                          
    println!("connect_timeout returned Ok(())");                                                                                                                                                                                                                        
    println!("peer_addr: {:?}", socket.peer_addr());                                                                                                                                                                                                                    
}                                                                                                                                                                                                                                                                                          

Output on the affected machine:

Socket connected
Err(Os { code: 57, kind: NotConnected, message: "Socket is not connected" })

Environment

  • macOS 15.7.3 (aarch64)
  • socket2 0.6.3
  • IPv6 TCP socket connecting to a remote server
  • The machine has a global IPv6 address and a default IPv6 route via the physical interface
  • A VPN is active that captures IPv4 traffic (via a utun interface) but does not carry IPv6

In that particular environment, decomposing Socket::connect_timeout step by step the issue is in the fast path (https://github.com/rust-lang/socket2/blob/master/src/socket.rs#L217). The non-blocking connect() call returns Ok(0) instead of Err(EINPROGRESS):

set_nonblocking(true)                                                                                                                                                                                                                                                   
connect()  → Ok(())     ← should be Err(EINPROGRESS) for a remote TCP address                                                                                                                                                                                           
set_nonblocking(false)                                                                                                                                                                                                                                                  
// connect_timeout returns Ok(()) here, poll_connect is never called                                                                                                                                                                                                    

This happens consistently (10/10 attempts) on this machine when the VPN is active.

The same network configuration on Linux works correctly: connect() returns EINPROGRESS in both cases (with or without VPN)

There's something I am missing about how connect_timeout should be used?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions