blob: 43aacce5dc99917b301c4c78aecb934afa8421df [file]
/*
* 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 */