syd 3.54.1

rock-solid application kernel
Documentation
//
// Syd: rock-solid application kernel
// src/kernel/net/connect.rs: connect(2) handler
//
// Copyright (c) 2023, 2024, 2025, 2026 Ali Polatel <alip@chesswob.org>
//
// SPDX-License-Identifier: GPL-3.0

// SAFETY: This module has been liberated from unsafe code!
#![forbid(unsafe_code)]

use libseccomp::ScmpNotifResp;
use nix::{errno::Errno, sys::socket::SockaddrStorage};

use crate::{
    cache::UnixVal,
    compat::{sockaddr_family, AddressFamily},
    cookie::safe_connect,
    fd::{has_recv_timeout, SafeOwnedFd},
    kernel::net::handle_safe_bind,
    path::XPath,
    req::UNotifyEventRequest,
    unix::unix_path_bytes,
};

pub(crate) fn handle_connect(
    fd: SafeOwnedFd,
    addr: (SockaddrStorage, SockaddrStorage),
    request: &UNotifyEventRequest,
    allow_safe_bind: bool,
    is_nonblock: bool,
) -> Result<ScmpNotifResp, Errno> {
    let (addr, argaddr) = addr;

    // Record blocking call so it can get invalidated.
    let req = request.scmpreq;
    let is_blocking = if !is_nonblock {
        let ignore_restart = has_recv_timeout(&fd)?;

        // Record the blocking call.
        request.cache.add_sys_block(req, ignore_restart)?;

        true
    } else {
        false
    };

    // All done, call underlying system call.
    let result = safe_connect(&fd, &addr);

    // Remove invalidation record.
    if is_blocking {
        request.cache.del_sys_block(req.id)?;
    }

    if result.is_ok() {
        if allow_safe_bind
            && matches!(
                sockaddr_family(&addr),
                AddressFamily::Inet | AddressFamily::Inet6
            )
        {
            // Handle allow_safe_bind.
            // Ignore errors as connect has already succeeded.
            let _ = handle_safe_bind(request, &fd);
        } else if sockaddr_family(&addr) == AddressFamily::Unix {
            // Handle SO_PASSCRED inode tracking and getpeername(2).
            // Look up destination's device and inode to disambiguate at recv(2).
            // Ignore errors as connect(2) has already succeeded.
            let unix_peer = argaddr.as_unix_addr().filter(|u| u.path().is_some());
            let (ddev, dino) = unix_peer
                .and_then(unix_path_bytes)
                .map(XPath::from_bytes)
                .and_then(|path| request.lookup_unix_vfs_id(path).ok())
                .map_or((None, None), |(dev, ino)| (Some(dev), Some(ino)));
            let mut unix_val = UnixVal {
                peer: unix_peer.copied(),
                ..UnixVal::default()
            };
            if let (Some(dev), Some(ino)) = (ddev, dino) {
                if unix_val.dest.try_reserve(1).is_ok() {
                    unix_val.dest.push((dev, ino));
                }
            }
            let _ = request.add_unix(&fd, request.scmpreq.pid(), unix_val);
        }
    }

    result.map(|_| request.return_syscall(0))
}