| /* | |
| * socket related system call shims | |
| * | |
| * Copyright (c) 2013 Stacey D. Son | |
| * | |
| * SPDX-License-Identifier: GPL-2.0-or-later | |
| */ | |
| #ifndef BSD_SOCKET_H | |
| #define BSD_SOCKET_H | |
| #include <sys/types.h> | |
| #include <sys/socket.h> | |
| #include <sys/un.h> | |
| #include <netinet/in.h> | |
| #include "qemu-bsd.h" | |
| ssize_t safe_recvfrom(int s, void *buf, size_t len, int flags, | |
| struct sockaddr *restrict from, socklen_t *restrict fromlen); | |
| ssize_t safe_sendto(int s, const void *buf, size_t len, int flags, | |
| const struct sockaddr *to, socklen_t tolen); | |
| int safe_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, | |
| struct timeval *timeout); | |
| int safe_pselect(int nfds, fd_set *restrict readfds, | |
| fd_set *restrict writefds, fd_set *restrict exceptfds, | |
| const struct timespec *restrict timeout, | |
| const sigset_t *restrict newsigmask); | |
| /* bind(2) */ | |
| static inline abi_long do_bsd_bind(int sockfd, abi_ulong target_addr, | |
| socklen_t addrlen) | |
| { | |
| abi_long ret; | |
| void *addr; | |
| if ((int)addrlen < 0) { | |
| return -TARGET_EINVAL; | |
| } | |
| addr = alloca(addrlen + 1); | |
| ret = target_to_host_sockaddr(addr, target_addr, addrlen); | |
| if (is_error(ret)) { | |
| return ret; | |
| } | |
| return get_errno(bind(sockfd, addr, addrlen)); | |
| } | |
| /* connect(2) */ | |
| static inline abi_long do_bsd_connect(int sockfd, abi_ulong target_addr, | |
| socklen_t addrlen) | |
| { | |
| abi_long ret; | |
| void *addr; | |
| if ((int)addrlen < 0) { | |
| return -TARGET_EINVAL; | |
| } | |
| addr = alloca(addrlen + 1); | |
| ret = target_to_host_sockaddr(addr, target_addr, addrlen); | |
| if (is_error(ret)) { | |
| return ret; | |
| } | |
| return get_errno(connect(sockfd, addr, addrlen)); | |
| } | |
| /* accept(2) */ | |
| static inline abi_long do_bsd_accept(int fd, abi_ulong target_addr, | |
| abi_ulong target_addrlen_addr) | |
| { | |
| socklen_t addrlen; | |
| void *addr; | |
| abi_long ret; | |
| if (target_addr == 0) { | |
| return get_errno(accept(fd, NULL, NULL)); | |
| } | |
| /* return EINVAL if addrlen pointer is invalid */ | |
| if (get_user_u32(addrlen, target_addrlen_addr)) { | |
| return -TARGET_EINVAL; | |
| } | |
| if ((int)addrlen < 0) { | |
| return -TARGET_EINVAL; | |
| } | |
| if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) { | |
| return -TARGET_EINVAL; | |
| } | |
| addr = alloca(addrlen); | |
| ret = get_errno(accept(fd, addr, &addrlen)); | |
| if (!is_error(ret)) { | |
| if (is_error(host_to_target_sockaddr(target_addr, addr, addrlen))) { | |
| close(ret); | |
| ret = -TARGET_EFAULT; | |
| } else if (put_user_u32(addrlen, target_addrlen_addr)) { | |
| close(ret); | |
| ret = -TARGET_EFAULT; | |
| } | |
| } | |
| return ret; | |
| } | |
| /* getpeername(2) */ | |
| static inline abi_long do_bsd_getpeername(int fd, abi_ulong target_addr, | |
| abi_ulong target_addrlen_addr) | |
| { | |
| socklen_t addrlen; | |
| void *addr; | |
| abi_long ret; | |
| if (get_user_u32(addrlen, target_addrlen_addr)) { | |
| return -TARGET_EFAULT; | |
| } | |
| if ((int)addrlen < 0) { | |
| return -TARGET_EINVAL; | |
| } | |
| if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) { | |
| return -TARGET_EFAULT; | |
| } | |
| addr = alloca(addrlen); | |
| ret = get_errno(getpeername(fd, addr, &addrlen)); | |
| if (!is_error(ret)) { | |
| ret = host_to_target_sockaddr(target_addr, addr, addrlen); | |
| if (is_error(ret)) { | |
| ret = -TARGET_EFAULT; | |
| } else if (put_user_u32(addrlen, target_addrlen_addr)) { | |
| ret = -TARGET_EFAULT; | |
| } | |
| } | |
| return ret; | |
| } | |
| /* getsockname(2) */ | |
| static inline abi_long do_bsd_getsockname(int fd, abi_ulong target_addr, | |
| abi_ulong target_addrlen_addr) | |
| { | |
| socklen_t addrlen; | |
| void *addr; | |
| abi_long ret; | |
| if (get_user_u32(addrlen, target_addrlen_addr)) { | |
| return -TARGET_EFAULT; | |
| } | |
| if ((int)addrlen < 0) { | |
| return -TARGET_EINVAL; | |
| } | |
| if (!access_ok(VERIFY_WRITE, target_addr, addrlen)) { | |
| return -TARGET_EFAULT; | |
| } | |
| addr = alloca(addrlen); | |
| ret = get_errno(getsockname(fd, addr, &addrlen)); | |
| if (!is_error(ret)) { | |
| ret = host_to_target_sockaddr(target_addr, addr, addrlen); | |
| if (is_error(ret)) { | |
| ret = -TARGET_EFAULT; | |
| } else if (put_user_u32(addrlen, target_addrlen_addr)) { | |
| ret = -TARGET_EFAULT; | |
| } | |
| } | |
| return ret; | |
| } | |
| /* socketpair(2) */ | |
| static inline abi_long do_bsd_socketpair(int domain, int type, int protocol, | |
| abi_ulong target_tab_addr) | |
| { | |
| int tab[2]; | |
| abi_long ret; | |
| if (!access_ok(VERIFY_WRITE, target_tab_addr, sizeof(tab[0]) * 2)) { | |
| return -TARGET_EFAULT; | |
| } | |
| ret = get_errno(socketpair(domain, type, protocol, tab)); | |
| if (!is_error(ret)) { | |
| if (put_user_s32(tab[0], target_tab_addr) || | |
| put_user_s32(tab[1], target_tab_addr + sizeof(tab[0]))) { | |
| ret = -TARGET_EFAULT; | |
| } | |
| } | |
| return ret; | |
| } | |
| /* sendto(2) */ | |
| static inline abi_long do_bsd_sendto(int fd, abi_ulong msg, size_t len, | |
| int flags, abi_ulong target_addr, | |
| socklen_t addrlen) | |
| { | |
| struct sockaddr *saddr; | |
| void *host_msg; | |
| abi_long ret; | |
| if ((int)addrlen < 0) { | |
| return -TARGET_EINVAL; | |
| } | |
| if (len != 0) { | |
| host_msg = lock_user(VERIFY_READ, msg, len, 1); | |
| if (!host_msg) { | |
| return -TARGET_EFAULT; | |
| } | |
| } else { | |
| host_msg = NULL; | |
| } | |
| if (target_addr) { | |
| saddr = alloca(addrlen + 1); | |
| ret = target_to_host_sockaddr(saddr, target_addr, addrlen); | |
| if (is_error(ret)) { | |
| unlock_user(host_msg, msg, 0); | |
| return ret; | |
| } | |
| ret = get_errno(safe_sendto(fd, host_msg, len, flags, saddr, addrlen)); | |
| } else { | |
| ret = get_errno(send(fd, host_msg, len, flags)); | |
| } | |
| unlock_user(host_msg, msg, 0); | |
| return ret; | |
| } | |
| /* recvfrom(2) */ | |
| static inline abi_long do_bsd_recvfrom(int fd, abi_ulong msg, size_t len, | |
| int flags, abi_ulong target_addr, | |
| abi_ulong target_addrlen) | |
| { | |
| socklen_t addrlen; | |
| struct sockaddr *saddr; | |
| void *host_msg; | |
| abi_long ret; | |
| host_msg = lock_user(VERIFY_WRITE, msg, len, 0); | |
| if (!host_msg) { | |
| return -TARGET_EFAULT; | |
| } | |
| if (target_addr) { | |
| if (get_user_u32(addrlen, target_addrlen)) { | |
| ret = -TARGET_EFAULT; | |
| goto fail; | |
| } | |
| if ((int)addrlen < 0) { | |
| ret = -TARGET_EINVAL; | |
| goto fail; | |
| } | |
| saddr = alloca(addrlen + 1); | |
| ret = get_errno(safe_recvfrom(fd, host_msg, len, flags, saddr, | |
| &addrlen)); | |
| } else { | |
| saddr = NULL; /* To keep compiler quiet. */ | |
| ret = get_errno(recv(fd, host_msg, len, flags)); | |
| } | |
| if (!is_error(ret)) { | |
| if (target_addr) { | |
| if (is_error(host_to_target_sockaddr(target_addr, saddr, addrlen))) { | |
| ret = -TARGET_EFAULT; | |
| goto fail; | |
| } else if (put_user_u32(addrlen, target_addrlen)) { | |
| ret = -TARGET_EFAULT; | |
| goto fail; | |
| } | |
| } | |
| unlock_user(host_msg, msg, ret); | |
| } else { | |
| fail: | |
| unlock_user(host_msg, msg, 0); | |
| } | |
| return ret; | |
| } | |
| /* socket(2) */ | |
| static inline abi_long do_bsd_socket(abi_long domain, abi_long type, | |
| abi_long protocol) | |
| { | |
| return get_errno(socket(domain, type, protocol)); | |
| } | |
| /* shutdown(2) */ | |
| static inline abi_long do_bsd_shutdown(abi_long s, abi_long how) | |
| { | |
| return get_errno(shutdown(s, how)); | |
| } | |
| #endif /* BSD_SOCKET_H */ |