root/src/system/kernel/fs/socket.cpp
/*
 * Copyright 2009-2010, Axel Dörfler, axeld@pinc-software.de.
 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include <sys/socket.h>

#include <errno.h>
#include <limits.h>

#include <module.h>

#include <AutoDeleter.h>
#include <AutoDeleterDrivers.h>

#include <syscall_utils.h>

#include <fd.h>
#include <kernel.h>
#include <lock.h>
#include <syscall_restart.h>
#include <util/AutoLock.h>
#include <util/iovec_support.h>
#include <vfs.h>

#include <net_stack_interface.h>
#include <net_stat.h>


#define MAX_SOCKET_ADDRESS_LENGTH       (sizeof(sockaddr_storage))
#define MAX_SOCKET_OPTION_LENGTH        128
#define MAX_ANCILLARY_DATA_LENGTH       1024

#define GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor) \
        do {                                                                                            \
                status_t getError = get_socket_descriptor(fd, kernel, descriptor); \
                if (getError != B_OK)                                                   \
                        return getError;                                                        \
        } while (false)

#define FD_SOCKET(descriptor) ((net_socket*)descriptor->cookie)


static net_stack_interface_module_info* sStackInterface = NULL;
static int32 sStackInterfaceConsumers = 0;
static rw_lock sLock = RW_LOCK_INITIALIZER("stack interface");


static net_stack_interface_module_info*
get_stack_interface_module()
{
        atomic_add(&sStackInterfaceConsumers, 1);

        ReadLocker readLocker(sLock);
        if (sStackInterface != NULL)
                return sStackInterface;

        readLocker.Unlock();
        WriteLocker writeLocker(sLock);
        if (sStackInterface != NULL)
                return sStackInterface;

        // load module
        net_stack_interface_module_info* module;
        // TODO: Add driver settings option to load the userland net stack.
        status_t error = get_module(NET_STACK_INTERFACE_MODULE_NAME,
                (module_info**)&module);
        if (error == B_OK)
                sStackInterface = module;

        if (sStackInterface == NULL)
                atomic_add(&sStackInterfaceConsumers, -1);

        return sStackInterface;
}


static void
put_stack_interface_module()
{
        if (atomic_add(&sStackInterfaceConsumers, -1) != 1)
                return;

        // Keep the stack loaded on non-KDEBUG kernels.
#if KDEBUG
        WriteLocker _(sLock);
        if (atomic_get(&sStackInterfaceConsumers) > 0)
                return;
        if (sStackInterface == NULL)
                return;

        put_module(NET_STACK_INTERFACE_MODULE_NAME);
        sStackInterface = NULL;
#endif
}


static status_t
copy_address_from_userland(const sockaddr* userAddress, socklen_t addressLength,
        sockaddr_storage* address)
{
        if (userAddress == NULL || addressLength < 0 || addressLength > MAX_SOCKET_ADDRESS_LENGTH)
                return B_BAD_VALUE;

        *address = {};
        if (!IS_USER_ADDRESS(userAddress)
                        || user_memcpy(address, userAddress, addressLength) != B_OK) {
                return B_BAD_ADDRESS;
        }

        address->ss_len = addressLength;
                // make sure the sa_len field is set correctly

        return B_OK;
}


static status_t
prepare_userland_address_result(struct sockaddr*& userAddress,
        socklen_t* _addressLength, socklen_t& addressLength, bool addressRequired)
{
        // check parameters
        if (_addressLength == NULL)
                return B_BAD_VALUE;
        if (userAddress == NULL) {
                if (addressRequired)
                        return B_BAD_VALUE;
        } else {
                if (!IS_USER_ADDRESS(_addressLength))
                        return B_BAD_ADDRESS;
                if (!IS_USER_ADDRESS(userAddress)) {
                        if (addressRequired)
                                return B_BAD_ADDRESS;
                        userAddress = (struct sockaddr*)(intptr_t)-1;
                }
        }

        // copy the buffer size from userland
        addressLength = 0;
        if (userAddress != NULL
                        && user_memcpy(&addressLength, _addressLength, sizeof(socklen_t))
                                != B_OK) {
                return B_BAD_ADDRESS;
        }

        if (addressLength > MAX_SOCKET_ADDRESS_LENGTH)
                addressLength = MAX_SOCKET_ADDRESS_LENGTH;

        return B_OK;
}


static status_t
copy_address_to_userland(const void* address, socklen_t addressLength,
        sockaddr* userAddress, socklen_t userAddressBufferSize,
        socklen_t* userAddressLength)
{
        // copy address size and address back to userland
        if (user_memcpy(userAddressLength, &addressLength,
                        sizeof(socklen_t)) != B_OK
                || (userAddress != NULL
                        && (!IS_USER_ADDRESS(userAddress) || user_memcpy(userAddress, address,
                                min_c(addressLength, userAddressBufferSize)) != B_OK))) {
                return B_BAD_ADDRESS;
        }

        return B_OK;
}


static status_t
prepare_userland_msghdr(const msghdr* userMessage, msghdr& message,
        iovec*& userVecs, MemoryDeleter& vecsDeleter, void*& userAddress,
        char* address)
{
        if (userMessage == NULL)
                return B_BAD_VALUE;

        // copy message from userland
        if (!IS_USER_ADDRESS(userMessage)
                        || user_memcpy(&message, userMessage, sizeof(msghdr)) != B_OK) {
                return B_BAD_ADDRESS;
        }

        userVecs = message.msg_iov;
        userAddress = message.msg_name;

        // copy iovecs from userland
        if (message.msg_iovlen < 0 || message.msg_iovlen > IOV_MAX)
                return EMSGSIZE;
        if (userVecs != NULL && message.msg_iovlen > 0) {
                iovec* vecs = (iovec*)malloc(sizeof(iovec) * message.msg_iovlen);
                if (vecs == NULL)
                        return B_NO_MEMORY;
                vecsDeleter.SetTo(vecs);

                status_t error = get_iovecs_from_user(message.msg_iov, message.msg_iovlen, vecs);
                if (error != B_OK)
                        return error;
                message.msg_iov = vecs;
        } else {
                message.msg_iov = NULL;
                message.msg_iovlen = 0;
        }

        // prepare the address field
        userAddress = message.msg_name;
        if (userAddress != NULL) {
                if (!IS_USER_ADDRESS(message.msg_name))
                        return B_BAD_ADDRESS;
                if (message.msg_namelen > MAX_SOCKET_ADDRESS_LENGTH)
                        message.msg_namelen = MAX_SOCKET_ADDRESS_LENGTH;

                memset(address, 0, MAX_SOCKET_ADDRESS_LENGTH);
                message.msg_name = address;
        }

        return B_OK;
}


// #pragma mark - socket file descriptor


static status_t
socket_read(struct file_descriptor *descriptor, off_t pos, void *buffer,
        size_t *_length)
{
        ssize_t bytesRead = sStackInterface->recv(FD_SOCKET(descriptor), buffer,
                *_length, 0);
        *_length = bytesRead >= 0 ? bytesRead : 0;
        return bytesRead >= 0 ? B_OK : bytesRead;
}


static status_t
socket_write(struct file_descriptor *descriptor, off_t pos, const void *buffer,
        size_t *_length)
{
        ssize_t bytesWritten = sStackInterface->send(FD_SOCKET(descriptor), buffer,
                *_length, 0);
        *_length = bytesWritten >= 0 ? bytesWritten : 0;
        return bytesWritten >= 0 ? B_OK : bytesWritten;
}


static ssize_t
socket_readv(struct file_descriptor *descriptor, off_t pos,
        const struct iovec *vecs, int count)
{
        struct msghdr message = {};
        message.msg_iov = (struct iovec*)vecs;
        message.msg_iovlen = count;
        return sStackInterface->recvmsg(FD_SOCKET(descriptor), &message, 0);
}


static ssize_t
socket_writev(struct file_descriptor *descriptor, off_t pos,
        const struct iovec *vecs, int count)
{
        struct msghdr message = {};
        message.msg_iov = (struct iovec*)vecs;
        message.msg_iovlen = count;
        return sStackInterface->sendmsg(FD_SOCKET(descriptor), &message, 0);
}


static status_t
socket_ioctl(struct file_descriptor *descriptor, ulong op, void *buffer,
        size_t length)
{
        return sStackInterface->ioctl(FD_SOCKET(descriptor), op, buffer, length);
}


static status_t
socket_set_flags(struct file_descriptor *descriptor, int flags)
{
        // we ignore O_APPEND, but O_NONBLOCK we need to translate
        uint32 op = (flags & O_NONBLOCK) != 0
                ? B_SET_NONBLOCKING_IO : B_SET_BLOCKING_IO;

        return sStackInterface->ioctl(FD_SOCKET(descriptor), op, NULL, 0);
}


static status_t
socket_select(struct file_descriptor *descriptor, uint8 event,
        struct selectsync *sync)
{
        return sStackInterface->select(FD_SOCKET(descriptor), event, sync);
}


static status_t
socket_deselect(struct file_descriptor *descriptor, uint8 event,
        struct selectsync *sync)
{
        return sStackInterface->deselect(FD_SOCKET(descriptor), event, sync);
}


static status_t
socket_read_stat(struct file_descriptor *descriptor, struct stat *st)
{
        st->st_dev = 0;
        st->st_ino = (addr_t)descriptor->cookie;
        st->st_mode = S_IFSOCK | 0666;
        st->st_nlink = 1;
        st->st_uid = 0;
        st->st_gid = 0;
        st->st_size = 0;
        st->st_rdev = 0;
        st->st_blksize = 1024;  // use MTU for datagram sockets?
        st->st_type = 0;

        timespec now;
        now.tv_sec = time(NULL);
        now.tv_nsec = 0;

        st->st_atim = now;
        st->st_mtim = now;
        st->st_ctim = now;
        st->st_crtim = now;

        return B_OK;
}


static status_t
socket_close(struct file_descriptor *descriptor)
{
        return sStackInterface->close(FD_SOCKET(descriptor));
}


static void
socket_free(struct file_descriptor *descriptor)
{
        sStackInterface->free(FD_SOCKET(descriptor));
        put_stack_interface_module();
}


static struct fd_ops sSocketFDOps = {
        &socket_close,
        &socket_free,
        &socket_read,
        &socket_write,
        &socket_readv,
        &socket_writev,
        NULL,   // fd_seek
        &socket_ioctl,
        &socket_set_flags,
        &socket_select,
        &socket_deselect,
        NULL,   // fd_read_dir
        NULL,   // fd_rewind_dir
        &socket_read_stat,
        NULL,   // fd_write_stat
};


static status_t
get_socket_descriptor(int fd, bool kernel, file_descriptor*& descriptor)
{
        if (fd < 0)
                return EBADF;

        descriptor = get_fd(get_current_io_context(kernel), fd);
        if (descriptor == NULL)
                return EBADF;

        if (descriptor->ops != &sSocketFDOps) {
                put_fd(descriptor);
                return ENOTSOCK;
        }

        return B_OK;
}


static int
create_socket_fd(net_socket* socket, int flags, bool kernel)
{
        // Get the socket's non-blocking flag, so we can set the respective
        // open mode flag.
        int32 nonBlock;
        socklen_t nonBlockLen = sizeof(int32);
        status_t error = sStackInterface->getsockopt(socket, SOL_SOCKET,
                SO_NONBLOCK, &nonBlock, &nonBlockLen);
        if (error != B_OK)
                return error;
        int oflags = 0;
        if ((flags & SOCK_CLOEXEC) != 0)
                oflags |= O_CLOEXEC;
        if ((flags & SOCK_CLOFORK) != 0)
                oflags |= O_CLOFORK;
        if ((flags & SOCK_NONBLOCK) != 0 || nonBlock)
                oflags |= O_NONBLOCK;

        // allocate a file descriptor
        file_descriptor* descriptor = alloc_fd();
        if (descriptor == NULL)
                return B_NO_MEMORY;

        // init it
        descriptor->ops = &sSocketFDOps;
        descriptor->cookie = socket;
        descriptor->open_mode = O_RDWR | oflags;

        // publish it
        io_context* context = get_current_io_context(kernel);
        int fd = new_fd(context, descriptor);
        if (fd < 0) {
                descriptor->ops = NULL;
                put_fd(descriptor);
                return fd;
        }

        rw_lock_write_lock(&context->lock);
        fd_set_close_on_exec(context, fd, (oflags & O_CLOEXEC) != 0);
        fd_set_close_on_fork(context, fd, (oflags & O_CLOFORK) != 0);
        rw_lock_write_unlock(&context->lock);

        return fd;
}


// #pragma mark - common sockets API implementation


static int
common_socket(int family, int type, int protocol, bool kernel)
{
        if (!get_stack_interface_module())
                return B_UNSUPPORTED;

        int sflags = type & (SOCK_CLOEXEC | SOCK_NONBLOCK | SOCK_CLOFORK);
        type &= ~(SOCK_CLOEXEC | SOCK_NONBLOCK | SOCK_CLOFORK);

        // create the socket
        net_socket* socket;
        status_t error = sStackInterface->open(family, type, protocol, &socket);
        if (error != B_OK) {
                put_stack_interface_module();
                return error;
        }

        // allocate the FD
        int fd = create_socket_fd(socket, sflags, kernel);
        if (fd < 0) {
                sStackInterface->close(socket);
                sStackInterface->free(socket);
                put_stack_interface_module();
        }

        return fd;
}


static status_t
common_bind(int fd, const struct sockaddr *address, socklen_t addressLength,
        bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->bind(FD_SOCKET(descriptor), address, addressLength);
}


static status_t
common_shutdown(int fd, int how, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        if (how < SHUT_RD || how > SHUT_RDWR)
                return B_BAD_VALUE;

        return sStackInterface->shutdown(FD_SOCKET(descriptor), how);
}


static status_t
common_connect(int fd, const struct sockaddr *address,
        socklen_t addressLength, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->connect(FD_SOCKET(descriptor), address,
                addressLength);
}


static status_t
common_listen(int fd, int backlog, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->listen(FD_SOCKET(descriptor), backlog);
}


static int
common_accept(int fd, struct sockaddr *address, socklen_t *_addressLength, int flags,
        bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        if ((flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK | SOCK_CLOFORK)) != 0)
                RETURN_AND_SET_ERRNO(B_BAD_VALUE);

        net_socket* acceptedSocket;
        status_t error = sStackInterface->accept(FD_SOCKET(descriptor), address,
                _addressLength, &acceptedSocket);
        if (error != B_OK)
                return error;

        // allocate the FD
        int acceptedFD = create_socket_fd(acceptedSocket, flags, kernel);
        if (acceptedFD < 0) {
                sStackInterface->close(acceptedSocket);
                sStackInterface->free(acceptedSocket);
        } else {
                // we need a reference for the new FD
                get_stack_interface_module();
        }

        return acceptedFD;
}


static ssize_t
common_recv(int fd, void *data, size_t length, int flags, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->recv(FD_SOCKET(descriptor), data, length, flags);
}


static ssize_t
common_recvfrom(int fd, void *data, size_t length, int flags,
        struct sockaddr *address, socklen_t *_addressLength, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->recvfrom(FD_SOCKET(descriptor), data, length,
                flags, address, _addressLength);
}


static ssize_t
common_recvmsg(int fd, struct msghdr *message, int flags, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->recvmsg(FD_SOCKET(descriptor), message, flags);
}


static ssize_t
common_send(int fd, const void *data, size_t length, int flags, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->send(FD_SOCKET(descriptor), data, length, flags);
}


static ssize_t
common_sendto(int fd, const void *data, size_t length, int flags,
        const struct sockaddr *address, socklen_t addressLength, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->sendto(FD_SOCKET(descriptor), data, length, flags,
                address, addressLength);
}


static ssize_t
common_sendmsg(int fd, const struct msghdr *message, int flags, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->sendmsg(FD_SOCKET(descriptor), message, flags);
}


static status_t
common_getsockopt(int fd, int level, int option, void *value,
        socklen_t *_length, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->getsockopt(FD_SOCKET(descriptor), level, option,
                value, _length);
}


static status_t
common_setsockopt(int fd, int level, int option, const void *value,
        socklen_t length, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->setsockopt(FD_SOCKET(descriptor), level, option,
                value, length);
}


static status_t
common_getpeername(int fd, struct sockaddr *address,
        socklen_t *_addressLength, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->getpeername(FD_SOCKET(descriptor), address,
                _addressLength);
}


static status_t
common_getsockname(int fd, struct sockaddr *address,
        socklen_t *_addressLength, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->getsockname(FD_SOCKET(descriptor), address,
                _addressLength);
}


static int
common_sockatmark(int fd, bool kernel)
{
        file_descriptor* descriptor;
        GET_SOCKET_FD_OR_RETURN(fd, kernel, descriptor);
        FileDescriptorPutter _(descriptor);

        return sStackInterface->sockatmark(FD_SOCKET(descriptor));
}


static status_t
common_socketpair(int family, int type, int protocol, int fds[2], bool kernel)
{
        if (!get_stack_interface_module())
                return B_UNSUPPORTED;

        int sflags = type & (SOCK_CLOEXEC | SOCK_NONBLOCK | SOCK_CLOFORK);
        type &= ~(SOCK_CLOEXEC | SOCK_NONBLOCK | SOCK_CLOFORK);

        net_socket* sockets[2];
        status_t error = sStackInterface->socketpair(family, type, protocol,
                sockets);
        if (error != B_OK) {
                put_stack_interface_module();
                return error;
        }

        // allocate the FDs
        for (int i = 0; i < 2; i++) {
                fds[i] = create_socket_fd(sockets[i], sflags, kernel);
                if (fds[i] < 0) {
                        sStackInterface->close(sockets[i]);
                        sStackInterface->free(sockets[i]);
                        put_stack_interface_module();
                        return fds[i];
                }
        }

        // We need another reference for the second socket
        get_stack_interface_module();
        return B_OK;
}


static status_t
common_get_next_socket_stat(int family, uint32 *cookie, struct net_stat *stat)
{
        if (!get_stack_interface_module())
                return B_UNSUPPORTED;

        status_t status = sStackInterface->get_next_socket_stat(family, cookie,
                stat);

        put_stack_interface_module();
        return status;
}


// #pragma mark - kernel sockets API


int
socket(int family, int type, int protocol)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_socket(family, type, protocol, true));
}


int
bind(int socket, const struct sockaddr *address, socklen_t addressLength)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_bind(socket, address, addressLength, true));
}


int
shutdown(int socket, int how)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_shutdown(socket, how, true));
}


int
connect(int socket, const struct sockaddr *address, socklen_t addressLength)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_connect(socket, address, addressLength, true));
}


int
listen(int socket, int backlog)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_listen(socket, backlog, true));
}


int
accept(int socket, struct sockaddr *address, socklen_t *_addressLength)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_accept(socket, address, _addressLength, 0, true));
}


int
accept4(int socket, struct sockaddr *address, socklen_t *_addressLength, int flags)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_accept(socket, address, _addressLength, flags, true));
}


ssize_t
recv(int socket, void *data, size_t length, int flags)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_recv(socket, data, length, flags, true));
}


ssize_t
recvfrom(int socket, void *data, size_t length, int flags,
        struct sockaddr *address, socklen_t *_addressLength)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_recvfrom(socket, data, length, flags, address,
                _addressLength, true));
}


ssize_t
recvmsg(int socket, struct msghdr *message, int flags)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_recvmsg(socket, message, flags, true));
}


ssize_t
send(int socket, const void *data, size_t length, int flags)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_send(socket, data, length, flags, true));
}


ssize_t
sendto(int socket, const void *data, size_t length, int flags,
        const struct sockaddr *address, socklen_t addressLength)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_sendto(socket, data, length, flags, address,
                addressLength, true));
}


ssize_t
sendmsg(int socket, const struct msghdr *message, int flags)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_sendmsg(socket, message, flags, true));
}


int
getsockopt(int socket, int level, int option, void *value, socklen_t *_length)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_getsockopt(socket, level, option, value,
                _length, true));
}


int
setsockopt(int socket, int level, int option, const void *value,
        socklen_t length)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_setsockopt(socket, level, option, value,
                length, true));
}


int
getpeername(int socket, struct sockaddr *address, socklen_t *_addressLength)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_getpeername(socket, address, _addressLength,
                true));
}


int
getsockname(int socket, struct sockaddr *address, socklen_t *_addressLength)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_getsockname(socket, address, _addressLength,
                true));
}


int
sockatmark(int socket)
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_sockatmark(socket, true));
}


int
socketpair(int family, int type, int protocol, int socketVector[2])
{
        SyscallFlagUnsetter _;
        RETURN_AND_SET_ERRNO(common_socketpair(family, type, protocol,
                socketVector, true));
}


// #pragma mark - syscalls


int
_user_socket(int family, int type, int protocol)
{
        SyscallRestartWrapper<int> result;
        return result = common_socket(family, type, protocol, false);
}


status_t
_user_bind(int socket, const struct sockaddr *userAddress,
        socklen_t addressLength)
{
        sockaddr_storage address;
        status_t error = copy_address_from_userland(userAddress, addressLength, &address);
        if (error != B_OK)
                return error;

        SyscallRestartWrapper<status_t> result;
        return result = common_bind(socket, (sockaddr*)&address, addressLength,
                false);
}


status_t
_user_shutdown_socket(int socket, int how)
{
        SyscallRestartWrapper<status_t> error;
        return error = common_shutdown(socket, how, false);
}


status_t
_user_connect(int socket, const struct sockaddr *userAddress,
        socklen_t addressLength)
{
        sockaddr_storage address;
        status_t error = copy_address_from_userland(userAddress, addressLength, &address);
        if (error != B_OK)
                return error;

        SyscallRestartWrapper<status_t> result;
        return result = common_connect(socket, (sockaddr*)&address, addressLength,
                false);
}


status_t
_user_listen(int socket, int backlog)
{
        SyscallRestartWrapper<status_t> error;
        return error = common_listen(socket, backlog, false);
}


int
_user_accept(int socket, struct sockaddr *userAddress,
        socklen_t *_addressLength, int flags)
{
        // check parameters
        socklen_t addressLength = 0;
        status_t error = prepare_userland_address_result(userAddress,
                _addressLength, addressLength, false);
        if (error != B_OK)
                return error;
        const socklen_t userAddressBufferSize = addressLength;

        // accept()
        SyscallRestartWrapper<int> result;
        char address[MAX_SOCKET_ADDRESS_LENGTH] = {};
        result = common_accept(socket,
                userAddress != NULL ? (sockaddr*)address : NULL, &addressLength, flags, false);

        // copy address size and address back to userland
        if (copy_address_to_userland(address, addressLength, userAddress,
                        userAddressBufferSize, _addressLength) != B_OK) {
                _user_close(result);
                return B_BAD_ADDRESS;
        }

        return result;
}


ssize_t
_user_recv(int socket, void *data, size_t length, int flags)
{
        if (length > 0 && (data == NULL || !is_user_address_range(data, length)))
                return B_BAD_ADDRESS;

        SyscallRestartWrapper<ssize_t> result;
        return result = common_recv(socket, data, length, flags, false);
}


ssize_t
_user_recvfrom(int socket, void *data, size_t length, int flags,
        struct sockaddr *userAddress, socklen_t *_addressLength)
{
        if (length > 0 && (data == NULL || !is_user_address_range(data, length)))
                return B_BAD_ADDRESS;

        // check parameters
        socklen_t addressLength = 0;
        status_t error = prepare_userland_address_result(userAddress,
                _addressLength, addressLength, false);
        if (error != B_OK)
                return error;
        const socklen_t userAddressBufferSize = addressLength;

        // recvfrom()
        SyscallRestartWrapper<ssize_t> result;
        char address[MAX_SOCKET_ADDRESS_LENGTH] = {};
        result = common_recvfrom(socket, data, length, flags,
                userAddress != NULL ? (sockaddr*)address : NULL, &addressLength, false);
        if (result < 0)
                return result;

        // copy address size and address back to userland
        if (copy_address_to_userland(address, addressLength, userAddress,
                        userAddressBufferSize, _addressLength) != B_OK) {
                return B_BAD_ADDRESS;
        }

        return result;
}


ssize_t
_user_recvmsg(int socket, struct msghdr *userMessage, int flags)
{
        // copy message from userland
        msghdr message;
        iovec* userVecs;
        MemoryDeleter vecsDeleter;
        void* userAddress;
        char address[MAX_SOCKET_ADDRESS_LENGTH];

        status_t error = prepare_userland_msghdr(userMessage, message, userVecs,
                vecsDeleter, userAddress, address);
        if (error != B_OK)
                return error;

        // prepare a buffer for ancillary data
        MemoryDeleter ancillaryDeleter;
        void* ancillary = NULL;
        void* userAncillary = message.msg_control;
        if (userAncillary != NULL) {
                if (!IS_USER_ADDRESS(userAncillary))
                        return B_BAD_ADDRESS;
                if (message.msg_controllen < 0)
                        return B_BAD_VALUE;
                if (message.msg_controllen > MAX_ANCILLARY_DATA_LENGTH)
                        message.msg_controllen = MAX_ANCILLARY_DATA_LENGTH;

                message.msg_control = ancillary = malloc(message.msg_controllen);
                if (message.msg_control == NULL)
                        return B_NO_MEMORY;

                ancillaryDeleter.SetTo(ancillary);
        }

        // recvmsg()
        SyscallRestartWrapper<ssize_t> result;
        result = common_recvmsg(socket, &message, flags, false);
        if (result < 0)
                return result;

        // copy the address, the ancillary data, and the message header back to
        // userland
        message.msg_name = userAddress;
        message.msg_iov = userVecs;
        message.msg_control = userAncillary;
        if ((userAddress != NULL && user_memcpy(userAddress, address,
                                message.msg_namelen) != B_OK)
                || (userAncillary != NULL && user_memcpy(userAncillary, ancillary,
                                message.msg_controllen) != B_OK)
                || user_memcpy(userMessage, &message, sizeof(msghdr)) != B_OK) {
                return B_BAD_ADDRESS;
        }

        return result;
}


ssize_t
_user_send(int socket, const void *data, size_t length, int flags)
{
        if (length > 0 && (data == NULL || !is_user_address_range(data, length)))
                return B_BAD_ADDRESS;

        SyscallRestartWrapper<ssize_t> result;
        return result = common_send(socket, data, length, flags, false);
}


ssize_t
_user_sendto(int socket, const void *data, size_t length, int flags,
        const struct sockaddr *userAddress, socklen_t addressLength)
{
        if (length > 0 && (data == NULL || !is_user_address_range(data, length)))
                return B_BAD_ADDRESS;

        // copy address from userland
        sockaddr_storage address;
        if (userAddress != NULL) {
                status_t error = copy_address_from_userland(userAddress, addressLength, &address);
                if (error != B_OK)
                        return error;
        } else {
                addressLength = 0;
        }

        // sendto()
        SyscallRestartWrapper<ssize_t> result;
        return result = common_sendto(socket, data, length, flags,
                userAddress != NULL ? (sockaddr*)&address : NULL, addressLength, false);
}


ssize_t
_user_sendmsg(int socket, const struct msghdr *userMessage, int flags)
{
        // copy message from userland
        msghdr message;
        iovec* userVecs;
        MemoryDeleter vecsDeleter;
        void* userAddress;
        char address[MAX_SOCKET_ADDRESS_LENGTH];

        status_t error = prepare_userland_msghdr(userMessage, message, userVecs,
                vecsDeleter, userAddress, address);
        if (error != B_OK)
                return error;

        // copy the address from userland
        if (userAddress != NULL
                        && user_memcpy(address, userAddress, message.msg_namelen) != B_OK) {
                return B_BAD_ADDRESS;
        }

        // copy ancillary data from userland
        MemoryDeleter ancillaryDeleter;
        void* userAncillary = message.msg_control;
        if (userAncillary != NULL) {
                if (!IS_USER_ADDRESS(userAncillary))
                        return B_BAD_ADDRESS;
                if (message.msg_controllen < 0
                                || message.msg_controllen > MAX_ANCILLARY_DATA_LENGTH) {
                        return B_BAD_VALUE;
                }

                message.msg_control = malloc(message.msg_controllen);
                if (message.msg_control == NULL)
                        return B_NO_MEMORY;
                ancillaryDeleter.SetTo(message.msg_control);

                if (user_memcpy(message.msg_control, userAncillary,
                                message.msg_controllen) != B_OK) {
                        return B_BAD_ADDRESS;
                }
        }

        // sendmsg()
        SyscallRestartWrapper<ssize_t> result;
        return result = common_sendmsg(socket, &message, flags, false);
}


status_t
_user_getsockopt(int socket, int level, int option, void *userValue,
        socklen_t *_length)
{
        // check params
        if (userValue == NULL || _length == NULL)
                return B_BAD_VALUE;
        if (!IS_USER_ADDRESS(userValue) || !IS_USER_ADDRESS(_length))
                return B_BAD_ADDRESS;

        // copy length from userland
        socklen_t length;
        if (user_memcpy(&length, _length, sizeof(socklen_t)) != B_OK)
                return B_BAD_ADDRESS;

        if (length > MAX_SOCKET_OPTION_LENGTH)
                return B_BAD_VALUE;

        // getsockopt()
        char value[MAX_SOCKET_OPTION_LENGTH];
        SyscallRestartWrapper<status_t> error;
        error = common_getsockopt(socket, level, option, value, &length,
                false);
        if (error != B_OK)
                return error;

        // copy value back to userland
        if (user_memcpy(userValue, value, length) != B_OK)
                return B_BAD_ADDRESS;

        return B_OK;
}


status_t
_user_setsockopt(int socket, int level, int option, const void *userValue,
        socklen_t length)
{
        // check params
        if (userValue == NULL || length > MAX_SOCKET_OPTION_LENGTH)
                return B_BAD_VALUE;

        // copy value from userland
        char value[MAX_SOCKET_OPTION_LENGTH];
        if (!IS_USER_ADDRESS(userValue)
                        || user_memcpy(value, userValue, length) != B_OK) {
                return B_BAD_ADDRESS;
        }

        // setsockopt();
        SyscallRestartWrapper<status_t> error;
        return error = common_setsockopt(socket, level, option, value, length,
                false);
}


status_t
_user_getpeername(int socket, struct sockaddr *userAddress,
        socklen_t *_addressLength)
{
        // check parameters
        socklen_t addressLength = 0;
        SyscallRestartWrapper<status_t> error;
        error = prepare_userland_address_result(userAddress, _addressLength,
                addressLength, true);
        if (error != B_OK)
                return error;
        const socklen_t userAddressBufferSize = addressLength;

        // getpeername()
        char address[MAX_SOCKET_ADDRESS_LENGTH] = {};
        error = common_getpeername(socket, (sockaddr*)address, &addressLength,
                false);
        if (error != B_OK)
                return error;

        // copy address size and address back to userland
        if (copy_address_to_userland(address, addressLength, userAddress,
                        userAddressBufferSize, _addressLength) != B_OK) {
                return B_BAD_ADDRESS;
        }

        return B_OK;
}


status_t
_user_getsockname(int socket, struct sockaddr *userAddress,
        socklen_t *_addressLength)
{
        // check parameters
        socklen_t addressLength = 0;
        SyscallRestartWrapper<status_t> error;
        error = prepare_userland_address_result(userAddress, _addressLength,
                addressLength, true);
        if (error != B_OK)
                return error;
        const socklen_t userAddressBufferSize = addressLength;

        // getsockname()
        char address[MAX_SOCKET_ADDRESS_LENGTH] = {};
        error = common_getsockname(socket, (sockaddr*)address, &addressLength,
                false);
        if (error != B_OK)
                return error;

        // copy address size and address back to userland
        if (copy_address_to_userland(address, addressLength, userAddress,
                        userAddressBufferSize, _addressLength) != B_OK) {
                return B_BAD_ADDRESS;
        }

        return B_OK;
}


int
_user_sockatmark(int socket)
{
        SyscallRestartWrapper<status_t> error;
        return error = common_sockatmark(socket, false);
}


status_t
_user_socketpair(int family, int type, int protocol, int *userSocketVector)
{
        // check parameters
        if (userSocketVector == NULL)
                return B_BAD_VALUE;
        if (!IS_USER_ADDRESS(userSocketVector))
                return B_BAD_ADDRESS;

        // socketpair()
        int socketVector[2];
        SyscallRestartWrapper<status_t> error;
        error = common_socketpair(family, type, protocol, socketVector, false);
        if (error != B_OK)
                return error;

        // copy FDs back to userland
        if (user_memcpy(userSocketVector, socketVector,
                        sizeof(socketVector)) != B_OK) {
                _user_close(socketVector[0]);
                _user_close(socketVector[1]);
                return B_BAD_ADDRESS;
        }

        return B_OK;
}


status_t
_user_get_next_socket_stat(int family, uint32 *_cookie, struct net_stat *_stat)
{
        // check parameters and copy cookie from userland
        if (_cookie == NULL || _stat == NULL)
                return B_BAD_VALUE;

        uint32 cookie;
        if (!IS_USER_ADDRESS(_stat) || !IS_USER_ADDRESS(_cookie)
                || user_memcpy(&cookie, _cookie, sizeof(cookie)) != B_OK) {
                return B_BAD_ADDRESS;
        }

        net_stat stat;
        SyscallRestartWrapper<status_t> error;
        error = common_get_next_socket_stat(family, &cookie, &stat);
        if (error != B_OK)
                return error;

        // copy cookie and data back to userland
        if (user_memcpy(_cookie, &cookie, sizeof(cookie)) != B_OK
                || user_memcpy(_stat, &stat, sizeof(net_stat)) != B_OK) {
                return B_BAD_ADDRESS;
        }

        return B_OK;
}