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

use std::os::fd::OwnedFd;

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

use crate::{
    compat::{fstatx, STATX_INO},
    confine::is_valid_ptr,
    req::UNotifyEventRequest,
};

pub(crate) fn handle_getsockname(
    fd: OwnedFd,
    request: &UNotifyEventRequest,
    args: &[u64; 6],
) -> Result<ScmpNotifResp, Errno> {
    // Get socket inode.
    let inode = fstatx(&fd, STATX_INO).map(|statx| statx.stx_ino)?;
    drop(fd); // Close our copy of the socket.

    // Lookup path by inode in sandbox unix map.
    // peer=None check filters out connect(2).
    let addr = if let Some(addr) = request.get_unix(inode).and_then(|unix| unix.addr) {
        addr
    } else {
        // Not a UNIX domain socket, continue.
        // SAFETY: No pointer-dereference in access check.
        return unsafe { Ok(request.continue_syscall()) };
    };

    // Determine address length.
    let addrlen = if args[2] != 0 {
        const SIZEOF_SOCKLEN_T: usize = size_of::<libc::socklen_t>();
        let mut buf = [0u8; SIZEOF_SOCKLEN_T];
        if request.read_mem(&mut buf, args[2], SIZEOF_SOCKLEN_T)? == SIZEOF_SOCKLEN_T {
            // libc defines socklen_t as u32,
            // however we should check for negative values
            // and return EINVAL as necessary.
            let len = i32::from_ne_bytes(buf);
            let len = libc::socklen_t::try_from(len).or(Err(Errno::EINVAL))?;
            if !is_valid_ptr(args[1], request.scmpreq.data.arch) {
                // address length is positive however address is NULL,
                // return EFAULT.
                return Err(Errno::EFAULT);
            }
            len
        } else {
            // Invalid/short read, assume invalid address length.
            return Err(Errno::EINVAL);
        }
    } else {
        // addrlen must not be NULL.
        return Err(Errno::EFAULT);
    };

    // Write address buffer.
    let ptr = addr.as_ptr() as *const u8;
    let len = addr.len() as usize;

    // Create a byte slice from the socket address pointer.
    // SAFETY: `ptr` is a valid pointer to memory of at least `len`
    // bytes, as it is provided by the `SockaddrStorage` instance.
    // The `SockaddrStorage` type ensures that the memory pointed to
    // by `ptr` is valid and properly aligned.
    let buf = unsafe { std::slice::from_raw_parts(ptr, len) };

    // Write the truncated socket address into memory.
    let len = len.min(addrlen as usize);
    request.write_mem(&buf[..len], args[1])?;

    // Convert address length into a vector of bytes.
    let buf = addr.len().to_ne_bytes();

    // Write address length into memory.
    request.write_mem(&buf, args[2])?;

    Ok(request.return_syscall(0))
}
