How do I replicate a C++ cast of Windows SOCKADDR_BTH to a SOCKADDR in Rust?

Viewed 618

I am writing a Bluetooth utility on Windows in Rust, using winsock2 (but happy to use any other libraries) and have hit a roadblock. The following C++ sample from the Windows Bluetooth Connection Sample is what I am using as a reference:.

SOCKADDR_BTH SockAddrBthServer;
# code to set SockAddrBthServer vals in here 
connect(LocalSocket, (struct sockaddr *) &SockAddrBthServer, sizeof(SOCKADDR_BTH));

The structs are defined like so:

typedef struct _SOCKADDR_BTH
{
    USHORT      addressFamily;  // Always AF_BTH
    BTH_ADDR    btAddr;         // Bluetooth device address
    GUID        serviceClassId; // [OPTIONAL] system will query SDP for port
    ULONG       port;           // RFCOMM channel or L2CAP PSM
} SOCKADDR_BTH, *PSOCKADDR_BTH;


typedef struct sockaddr {

#if (_WIN32_WINNT < 0x0600)
    u_short sa_family;
#else
    ADDRESS_FAMILY sa_family;           // Address family.
#endif //(_WIN32_WINNT < 0x0600)

    CHAR sa_data[14];                   // Up to 14 bytes of direct address.
} SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;

How do I replicate the (struct sockaddr *) &SockAddrBthServer cast from the connect line above in Rust? So far, I'm making use of the winapi, user32 and ws2_32 crates.

Here is the Rust version of the connect function from the ws2_32 crate.

pub unsafe extern "system" fn connect(s: SOCKET, name: *const SOCKADDR, namelen: c_int) -> c_int
1 Answers

You are overthinking the problem. If Windows says it's cool to cast a SOCKADDR_BTH pointer to a SOCKADDR pointer, then just do that. In Rust, you have to add one extra cast to leave the safe world of references and get to a raw pointer, then you can cast that to whatever you want:

use std::mem;

struct SomeErrorType;

fn example(LocalSocket: SOCKET) -> Result<SOCKADDR_BTH, SomeErrorType> {
    unsafe {
        let SockAddrBthServer: SOCKADDR_BTH = mem::uninitialized();
        let retval = connect(
            LocalSocket,
            &SockAddrBthServer as *const SOCKADDR_BTH as *const SOCKADDR,
            mem::size_of::<SOCKADDR_BTH>() as i32,
        );
        // PERFORM REAL ERROR CHECKING HERE
        if retval == 42 {
            Ok(SockAddrBthServer)
        } else {
            Err(SomeErrorType)
        }
    }
}

(Untested because I don't have a Windows machine handy)

Under the hood, this will only work as long as the initial members of SOCKADDR_BTH exactly match the members of SOCKADDR.

Related