blob: 15f36f8a11117ac96f364a1d52673158f8736c5d [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <poll.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <lib/fdio/io.h>
#include <lib/fdio/remoteio.h>
#include <lib/fdio/socket.h>
#include <lib/fdio/util.h>
#include "private-socket.h"
static bool is_rio_message_valid(zxsio_msg_t* msg) {
if ((msg->datalen > ZXSIO_PAYLOAD_SZ) ||
(msg->hcount > 0)) {
return false;
}
return true;
}
static bool is_rio_message_reply_valid(zxsio_msg_t* msg, uint32_t size) {
if ((size < ZXSIO_HDR_SZ) ||
(msg->datalen != (size - ZXSIO_HDR_SZ))) {
return false;
}
return is_rio_message_valid(msg);
}
zx_status_t zxsio_accept(fdio_t* io, zx_handle_t* s2) {
zxsio_t* sio = (zxsio_t*)io;
if (!(sio->flags & ZXSIO_DID_LISTEN)) {
return ZX_ERR_BAD_STATE;
}
zx_status_t r;
for (;;) {
r = zx_socket_accept(sio->s, s2);
if (r == ZX_ERR_SHOULD_WAIT) {
if (io->ioflag & IOFLAG_NONBLOCK) {
return ZX_ERR_SHOULD_WAIT;
}
// wait for an incoming connection
zx_signals_t pending;
r = zx_object_wait_one(sio->s, ZX_SOCKET_ACCEPT | ZX_SOCKET_PEER_CLOSED, ZX_TIME_INFINITE, &pending);
if (r < 0) {
return r;
}
if (pending & ZX_SOCKET_ACCEPT) {
continue;
}
if (pending & ZX_SOCKET_PEER_CLOSED) {
return ZX_ERR_PEER_CLOSED;
}
// impossible
return ZX_ERR_INTERNAL;
}
break;
}
return r;
}
static ssize_t zxsio_read_stream(fdio_t* io, void* data, size_t len) {
zxsio_t* sio = (zxsio_t*)io;
int nonblock = sio->io.ioflag & IOFLAG_NONBLOCK;
// TODO: let the generic read() to do this loop
for (;;) {
ssize_t r;
size_t bytes_read;
if ((r = zx_socket_read(sio->s, 0, data, len, &bytes_read)) == ZX_OK) {
// zx_socket_read() sets *actual to the number of bytes in the buffer when data is NULL
// and len is 0. read() should return 0 in that case.
if (len == 0) {
return 0;
} else {
return (ssize_t)bytes_read;
}
}
if (r == ZX_ERR_PEER_CLOSED || r == ZX_ERR_BAD_STATE) {
return 0;
} else if (r == ZX_ERR_SHOULD_WAIT && !nonblock) {
zx_signals_t pending;
r = zx_object_wait_one(sio->s,
ZX_SOCKET_READABLE | ZX_SOCKET_PEER_CLOSED | ZX_SOCKET_READ_DISABLED,
ZX_TIME_INFINITE, &pending);
if (r < 0) {
return r;
}
if (pending & ZX_SOCKET_READABLE) {
continue;
}
if (pending & (ZX_SOCKET_PEER_CLOSED | ZX_SOCKET_READ_DISABLED)) {
return 0;
}
// impossible
return ZX_ERR_INTERNAL;
}
return r;
}
}
static ssize_t zxsio_recvfrom(fdio_t* io, void* data, size_t len, int flags,
struct sockaddr* restrict addr,
socklen_t* restrict addrlen) {
struct iovec iov;
iov.iov_base = data;
iov.iov_len = len;
struct msghdr msg;
msg.msg_name = addr;
// the caller (recvfrom) checks if addrlen is NULL.
msg.msg_namelen = (addr == NULL) ? 0 : *addrlen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
ssize_t r = io->ops->recvmsg(io, &msg, flags);
if (addr != NULL)
*addrlen = msg.msg_namelen;
return r;
}
static ssize_t zxsio_write_stream(fdio_t* io, const void* data, size_t len) {
zxsio_t* sio = (zxsio_t*)io;
int nonblock = sio->io.ioflag & IOFLAG_NONBLOCK;
// TODO: let the generic write() to do this loop
for (;;) {
ssize_t r;
if ((r = zx_socket_write(sio->s, 0, data, len, &len)) == ZX_OK) {
return (ssize_t) len;
}
if (r == ZX_ERR_SHOULD_WAIT && !nonblock) {
zx_signals_t pending;
r = zx_object_wait_one(sio->s,
ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_DISABLED | ZX_SOCKET_PEER_CLOSED,
ZX_TIME_INFINITE, &pending);
if (r < 0) {
return r;
}
if (pending & (ZX_SOCKET_WRITE_DISABLED | ZX_SOCKET_PEER_CLOSED)) {
return ZX_ERR_PEER_CLOSED;
}
if (pending & ZX_SOCKET_WRITABLE) {
continue;
}
// impossible
return ZX_ERR_INTERNAL;
}
return r;
}
}
static ssize_t zxsio_sendto(fdio_t* io, const void* data, size_t len, int flags, const struct sockaddr* addr, socklen_t addrlen) {
struct iovec iov;
iov.iov_base = (void*)data;
iov.iov_len = len;
struct msghdr msg;
msg.msg_name = (void*)addr;
msg.msg_namelen = addrlen;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0; // this field is ignored
return io->ops->sendmsg(io, &msg, flags);
}
static ssize_t zxsio_recvmsg_stream(fdio_t* io, struct msghdr* msg, int flags) {
if (flags != 0) {
// TODO: support MSG_OOB
return ZX_ERR_NOT_SUPPORTED;
}
if (!(io->ioflag & IOFLAG_SOCKET_CONNECTED)) {
return ZX_ERR_BAD_STATE;
}
// we ignore msg_name and msg_namelen members.
// (this is a consistent behavior with other OS implementations for TCP protocol)
ssize_t total = 0;
for (int i = 0; i < msg->msg_iovlen; i++) {
struct iovec *iov = &msg->msg_iov[i];
ssize_t n = zxsio_read_stream(io, iov->iov_base, iov->iov_len);
if (n < 0) {
if (total == 0) {
return n;
}
return total;
}
total += n;
if ((size_t)n != iov->iov_len) {
break;
}
}
return total;
}
static ssize_t zxsio_sendmsg_stream(fdio_t* io, const struct msghdr* msg, int flags) {
if (flags != 0) {
// TODO: support MSG_OOB
return ZX_ERR_NOT_SUPPORTED;
}
// TODO: support flags and control messages
if (io->ioflag & IOFLAG_SOCKET_CONNECTED) {
// if connected, can't specify address
if (msg->msg_name != NULL || msg->msg_namelen != 0) {
return ZX_ERR_ALREADY_EXISTS;
}
} else {
return ZX_ERR_BAD_STATE;
}
ssize_t total = 0;
for (int i = 0; i < msg->msg_iovlen; i++) {
struct iovec *iov = &msg->msg_iov[i];
if (iov->iov_len <= 0) {
return ZX_ERR_INVALID_ARGS;
}
ssize_t n = zxsio_write_stream(io, iov->iov_base, iov->iov_len);
if (n < 0) {
return n;
}
total += n;
if ((size_t)n != iov->iov_len) {
break;
}
}
return total;
}
static zx_status_t zxsio_clone_stream(fdio_t* io, zx_handle_t* handles, uint32_t* types) {
// TODO: support unconnected sockets
if (!(io->ioflag & IOFLAG_SOCKET_CONNECTED)) {
return ZX_ERR_BAD_STATE;
}
zxsio_t* sio = (void*)io;
zx_status_t r = zx_handle_duplicate(sio->s, ZX_RIGHT_SAME_RIGHTS, handles);
if (r < 0) {
return r;
}
types[0] = PA_FDIO_SOCKET;
return 1;
}
static zx_status_t zxsio_unwrap_stream(fdio_t* io, zx_handle_t* handles, uint32_t* types) {
// TODO: support unconnected sockets
if (!(io->ioflag & IOFLAG_SOCKET_CONNECTED)) {
return ZX_ERR_BAD_STATE;
}
zxsio_t* sio = (void*)io;
zx_status_t r;
handles[0] = sio->s;
types[0] = PA_FDIO_SOCKET;
r = 1;
return r;
}
static void zxsio_wait_begin_stream(fdio_t* io, uint32_t events, zx_handle_t* handle, zx_signals_t* _signals) {
zxsio_t* sio = (void*)io;
*handle = sio->s;
// TODO: locking for flags/state
if (io->ioflag & IOFLAG_SOCKET_CONNECTING) {
// check the connection state
zx_signals_t observed;
zx_status_t r;
r = zx_object_wait_one(sio->s, ZXSIO_SIGNAL_CONNECTED, 0u,
&observed);
if (r == ZX_OK || r == ZX_ERR_TIMED_OUT) {
if (observed & ZXSIO_SIGNAL_CONNECTED) {
io->ioflag &= ~IOFLAG_SOCKET_CONNECTING;
io->ioflag |= IOFLAG_SOCKET_CONNECTED;
}
}
}
zx_signals_t signals = ZXSIO_SIGNAL_ERROR;
if (io->ioflag & IOFLAG_SOCKET_CONNECTED) {
// if socket is connected
if (events & POLLIN) {
signals |= ZX_SOCKET_READABLE | ZX_SOCKET_READ_DISABLED | ZX_SOCKET_PEER_CLOSED;
}
if (events & POLLOUT) {
signals |= ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_DISABLED;
}
} else {
// if socket is not connected
if (events & POLLIN) {
// signal when a listening socket gets an incoming connection
// or a connecting socket gets connected and receives data
signals |= ZX_SOCKET_ACCEPT |
ZX_SOCKET_READABLE | ZX_SOCKET_READ_DISABLED | ZX_SOCKET_PEER_CLOSED;
}
if (events & POLLOUT) {
// signal when connect() operation is finished
signals |= ZXSIO_SIGNAL_OUTGOING;
}
}
if (events & POLLRDHUP) {
signals |= ZX_SOCKET_READ_DISABLED | ZX_SOCKET_PEER_CLOSED;
}
*_signals = signals;
}
static void zxsio_wait_end_stream(fdio_t* io, zx_signals_t signals, uint32_t* _events) {
// check the connection state
if (io->ioflag & IOFLAG_SOCKET_CONNECTING) {
if (signals & ZXSIO_SIGNAL_CONNECTED) {
io->ioflag &= ~IOFLAG_SOCKET_CONNECTING;
io->ioflag |= IOFLAG_SOCKET_CONNECTED;
}
}
uint32_t events = 0;
if (io->ioflag & IOFLAG_SOCKET_CONNECTED) {
if (signals & (ZX_SOCKET_READABLE | ZX_SOCKET_READ_DISABLED | ZX_SOCKET_PEER_CLOSED)) {
events |= POLLIN;
}
if (signals & (ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_DISABLED)) {
events |= POLLOUT;
}
} else {
if (signals & (ZX_SOCKET_ACCEPT | ZX_SOCKET_PEER_CLOSED)) {
events |= POLLIN;
}
if (signals & ZXSIO_SIGNAL_OUTGOING) {
events |= POLLOUT;
}
}
if (signals & ZXSIO_SIGNAL_ERROR) {
events |= POLLERR;
}
if (signals & (ZX_SOCKET_READ_DISABLED | ZX_SOCKET_PEER_CLOSED)) {
events |= POLLRDHUP;
}
*_events = events;
}
static ssize_t zxsio_posix_ioctl_stream(fdio_t* io, int req, va_list va) {
zxsio_t* sio = (zxsio_t*)io;
switch (req) {
case FIONREAD: {
zx_status_t r;
size_t avail;
if ((r = zx_socket_read(sio->s, 0, NULL, 0, &avail)) < 0) {
return r;
}
if (avail > INT_MAX) {
avail = INT_MAX;
}
int* actual = va_arg(va, int*);
*actual = avail;
return ZX_OK;
}
default:
return ZX_ERR_NOT_SUPPORTED;
}
}
static ssize_t zxsio_rx_dgram(fdio_t* io, void* buf, size_t buflen) {
return zxsio_read_stream(io, buf, buflen);
}
static ssize_t zxsio_tx_dgram(fdio_t* io, const void* buf, size_t buflen) {
zx_status_t r = zxsio_write_stream(io, buf, buflen);
return (r < 0) ? r : ZX_OK;
}
static ssize_t zxsio_recvmsg_dgram(fdio_t* io, struct msghdr* msg, int flags);
static ssize_t zxsio_sendmsg_dgram(fdio_t* io, const struct msghdr* msg, int flags);
static ssize_t zxsio_read_dgram(fdio_t* io, void* data, size_t len) {
struct iovec iov;
iov.iov_base = data;
iov.iov_len = len;
struct msghdr msg;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
return zxsio_recvmsg_dgram(io, &msg, 0);
}
static ssize_t zxsio_write_dgram(fdio_t* io, const void* data, size_t len) {
struct iovec iov;
iov.iov_base = (void*)data;
iov.iov_len = len;
struct msghdr msg;
msg.msg_name = NULL;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
return zxsio_sendmsg_dgram(io, &msg, 0);
}
static ssize_t zxsio_recvmsg_dgram(fdio_t* io, struct msghdr* msg, int flags) {
if (flags != 0) {
// TODO: support MSG_OOB
return ZX_ERR_NOT_SUPPORTED;
}
// Read 1 extra byte to detect if the buffer is too small to fit the whole
// packet, so we can set MSG_TRUNC flag if necessary.
size_t mlen = FDIO_SOCKET_MSG_HEADER_SIZE + 1;
for (int i = 0; i < msg->msg_iovlen; i++) {
struct iovec *iov = &msg->msg_iov[i];
if (iov->iov_len <= 0) {
return ZX_ERR_INVALID_ARGS;
}
mlen += iov->iov_len;
}
// TODO: avoid malloc
fdio_socket_msg_t* m = malloc(mlen);
ssize_t n = zxsio_rx_dgram(io, m, mlen);
if (n < 0) {
free(m);
return n;
}
if ((size_t)n < FDIO_SOCKET_MSG_HEADER_SIZE) {
free(m);
return ZX_ERR_INTERNAL;
}
n -= FDIO_SOCKET_MSG_HEADER_SIZE;
if (msg->msg_name != NULL) {
int bytes_to_copy = (msg->msg_namelen < m->addrlen) ? msg->msg_namelen : m->addrlen;
memcpy(msg->msg_name, &m->addr, bytes_to_copy);
}
msg->msg_namelen = m->addrlen;
msg->msg_flags = m->flags;
char* data = m->data;
size_t resid = n;
for (int i = 0; i < msg->msg_iovlen; i++) {
struct iovec *iov = &msg->msg_iov[i];
if (resid == 0) {
iov->iov_len = 0;
} else {
if (resid < iov->iov_len)
iov->iov_len = resid;
memcpy(iov->iov_base, data, iov->iov_len);
data += iov->iov_len;
resid -= iov->iov_len;
}
}
if (resid > 0) {
msg->msg_flags |= MSG_TRUNC;
n -= resid;
}
free(m);
return n;
}
static ssize_t zxsio_sendmsg_dgram(fdio_t* io, const struct msghdr* msg, int flags) {
if (flags != 0) {
// TODO: MSG_OOB
return ZX_ERR_NOT_SUPPORTED;
}
// TODO: support flags and control messages
if (io->ioflag & IOFLAG_SOCKET_CONNECTED) {
// if connected, can't specify address
if (msg->msg_name != NULL || msg->msg_namelen != 0) {
return ZX_ERR_ALREADY_EXISTS;
}
}
ssize_t n = 0;
for (int i = 0; i < msg->msg_iovlen; i++) {
struct iovec *iov = &msg->msg_iov[i];
if (iov->iov_len <= 0) {
return ZX_ERR_INVALID_ARGS;
}
n += iov->iov_len;
}
size_t mlen = n + FDIO_SOCKET_MSG_HEADER_SIZE;
// TODO: avoid malloc m
fdio_socket_msg_t* m = malloc(mlen);
if (msg->msg_name != NULL) {
memcpy(&m->addr, msg->msg_name, msg->msg_namelen);
}
m->addrlen = msg->msg_namelen;
m->flags = flags;
char* data = m->data;
for (int i = 0; i < msg->msg_iovlen; i++) {
struct iovec *iov = &msg->msg_iov[i];
memcpy(data, iov->iov_base, iov->iov_len);
data += iov->iov_len;
}
ssize_t r = zxsio_tx_dgram(io, m, mlen);
free(m);
return r == ZX_OK ? n : r;
}
static void zxsio_wait_begin_dgram(fdio_t* io, uint32_t events, zx_handle_t* handle, zx_signals_t* _signals) {
zxsio_t* sio = (void*)io;
*handle = sio->s;
zx_signals_t signals = ZXSIO_SIGNAL_ERROR;
if (events & POLLIN) {
signals |= ZX_SOCKET_READABLE | ZX_SOCKET_READ_DISABLED | ZX_SOCKET_PEER_CLOSED;
}
if (events & POLLOUT) {
signals |= ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_DISABLED;
}
if (events & POLLRDHUP) {
signals |= ZX_SOCKET_READ_DISABLED | ZX_SOCKET_PEER_CLOSED;
}
*_signals = signals;
}
static void zxsio_wait_end_dgram(fdio_t* io, zx_signals_t signals, uint32_t* _events) {
uint32_t events = 0;
if (signals & (ZX_SOCKET_READABLE | ZX_SOCKET_READ_DISABLED | ZX_SOCKET_PEER_CLOSED)) {
events |= POLLIN;
}
if (signals & (ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_DISABLED)) {
events |= POLLOUT;
}
if (signals & ZXSIO_SIGNAL_ERROR) {
events |= POLLERR;
}
if (signals & (ZX_SOCKET_READ_DISABLED | ZX_SOCKET_PEER_CLOSED)) {
events |= POLLRDHUP;
}
*_events = events;
}
static zx_status_t zxsio_write_control(zxsio_t* sio, zxsio_msg_t* msg) {
for (;;) {
ssize_t r;
size_t len = ZXSIO_HDR_SZ + msg->datalen;
if ((r = zx_socket_write(sio->s, ZX_SOCKET_CONTROL, msg, len, &len)) == ZX_OK) {
return (ssize_t) len;
}
// If the socket has no control plane then control messages are not
// supported.
if (r == ZX_ERR_BAD_STATE) {
return ZX_ERR_NOT_SUPPORTED;
}
if (r == ZX_ERR_SHOULD_WAIT) {
zx_signals_t pending;
r = zx_object_wait_one(sio->s,
ZX_SOCKET_CONTROL_WRITABLE | ZX_SOCKET_PEER_CLOSED,
ZX_TIME_INFINITE, &pending);
if (r < 0) {
return r;
}
if (pending & ZX_SOCKET_PEER_CLOSED) {
return ZX_ERR_PEER_CLOSED;
}
if (pending & ZX_SOCKET_CONTROL_WRITABLE) {
continue;
}
// impossible
return ZX_ERR_INTERNAL;
}
return r;
}
}
static ssize_t zxsio_read_control(zxsio_t* sio, void* data, size_t len) {
// TODO: let the generic read() to do this loop
for (;;) {
ssize_t r;
size_t bytes_read;
if ((r = zx_socket_read(sio->s, ZX_SOCKET_CONTROL, data, len, &bytes_read)) == ZX_OK) {
// zx_socket_read() sets *actual to the number of bytes in the buffer when data is NULL
// and len is 0. read() should return 0 in that case.
if (len == 0) {
return 0;
} else {
return (ssize_t)bytes_read;
}
}
if (r == ZX_ERR_PEER_CLOSED || r == ZX_ERR_BAD_STATE) {
return 0;
} else if (r == ZX_ERR_SHOULD_WAIT) {
zx_signals_t pending;
r = zx_object_wait_one(sio->s,
ZX_SOCKET_CONTROL_READABLE | ZX_SOCKET_PEER_CLOSED,
ZX_TIME_INFINITE, &pending);
if (r < 0) {
return r;
}
if (pending & ZX_SOCKET_CONTROL_READABLE) {
continue;
}
if (pending & ZX_SOCKET_PEER_CLOSED) {
return 0;
}
// impossible
return ZX_ERR_INTERNAL;
}
return r;
}
}
static zx_status_t zxsio_txn(zxsio_t* sio, zxsio_msg_t* msg) {
if (!is_rio_message_valid(msg)) {
return ZX_ERR_INVALID_ARGS;
}
zx_status_t r = zxsio_write_control(sio, msg);
if (r < 0)
return r;
const uint32_t request_op = msg->op;
r = zxsio_read_control(sio, msg, sizeof(*msg));
if (r < 0)
return r;
size_t dsize = (size_t)r;
// check for protocol errors
if (!is_rio_message_reply_valid(msg, dsize) || (msg->op != request_op)) {
return ZX_ERR_IO;
}
return msg->arg;
}
static zx_status_t zxsio_misc(fdio_t* io, uint32_t op, int64_t off,
uint32_t maxreply, void* ptr, size_t len) {
zxsio_t* sio = (zxsio_t*)io;
zxsio_msg_t msg;
zx_status_t r;
if ((len > ZXSIO_PAYLOAD_SZ) || (maxreply > ZXSIO_PAYLOAD_SZ)) {
return ZX_ERR_INVALID_ARGS;
}
switch (op) {
case ZXSIO_GETSOCKNAME:
case ZXSIO_GETPEERNAME:
case ZXSIO_GETSOCKOPT:
case ZXSIO_SETSOCKOPT:
case ZXSIO_CONNECT:
case ZXSIO_BIND:
case ZXSIO_LISTEN:
break;
default:
return ZX_ERR_NOT_SUPPORTED;
}
memset(&msg, 0, ZXSIO_HDR_SZ);
msg.op = op;
msg.arg = maxreply;
msg.arg2.off = off;
msg.datalen = len;
if (ptr && len > 0) {
memcpy(msg.data, ptr, len);
}
if ((r = zxsio_txn(sio, &msg)) < 0) {
return r;
}
if (msg.datalen > maxreply) {
return ZX_ERR_IO;
}
if (ptr && msg.datalen > 0) {
memcpy(ptr, msg.data, msg.datalen);
}
if (op == ZXSIO_LISTEN && r == ZX_OK) {
sio->flags |= ZXSIO_DID_LISTEN;
}
return r;
}
static zx_status_t zxsio_close(fdio_t* io) {
zxsio_t* sio = (zxsio_t*)io;
zxsio_msg_t msg;
zx_status_t r;
memset(&msg, 0, ZXSIO_HDR_SZ);
msg.op = ZXSIO_CLOSE;
r = zxsio_txn(sio, &msg);
zx_handle_t h = sio->s;
sio->s = 0;
zx_handle_close(h);
return r;
}
static ssize_t zxsio_ioctl(fdio_t* io, uint32_t op, const void* in_buf,
size_t in_len, void* out_buf, size_t out_len) {
zxsio_t* sio = (zxsio_t*)io;
const uint8_t* data = in_buf;
zx_status_t r = 0;
zxsio_msg_t msg;
if (in_len > ZXSIO_PAYLOAD_SZ || out_len > ZXSIO_PAYLOAD_SZ) {
return ZX_ERR_INVALID_ARGS;
}
if (IOCTL_KIND(op) != IOCTL_KIND_DEFAULT) {
return ZX_ERR_NOT_SUPPORTED;
}
memset(&msg, 0, ZXSIO_HDR_SZ);
msg.op = ZXSIO_IOCTL;
msg.datalen = in_len;
msg.arg = out_len;
msg.arg2.op = op;
memcpy(msg.data, data, in_len);
if ((r = zxsio_txn(sio, &msg)) < 0) {
return r;
}
size_t copy_len = msg.datalen;
if (msg.datalen > out_len) {
copy_len = out_len;
}
memcpy(out_buf, msg.data, copy_len);
return r;
}
static zx_status_t fdio_socket_shutdown(fdio_t* io, int how) {
if (!(io->ioflag & IOFLAG_SOCKET_CONNECTED)) {
return ZX_ERR_BAD_STATE;
}
zxsio_t* sio = (zxsio_t*)io;
if (how == SHUT_WR || how == SHUT_RDWR) {
// netstack expects this user signal to be set - raise it to keep that code working until
// it learns about the read/write disabled signals.
zx_object_signal_peer(sio->s, 0u, ZXSIO_SIGNAL_HALFCLOSED);
}
uint32_t options = 0;
switch (how) {
case SHUT_RD:
options = ZX_SOCKET_SHUTDOWN_READ;
break;
case SHUT_WR:
options = ZX_SOCKET_SHUTDOWN_WRITE;
break;
case SHUT_RDWR:
options = ZX_SOCKET_SHUTDOWN_READ | ZX_SOCKET_SHUTDOWN_WRITE;
break;
}
return zx_socket_write(sio->s, options, NULL, 0, NULL);
}
static fdio_ops_t fdio_socket_stream_ops = {
.read = zxsio_read_stream,
.read_at = fdio_default_read_at,
.write = zxsio_write_stream,
.write_at = fdio_default_write_at,
.seek = fdio_default_seek,
.misc = zxsio_misc,
.close = zxsio_close,
.open = fdio_default_open,
.clone = zxsio_clone_stream,
.ioctl = zxsio_ioctl,
.wait_begin = zxsio_wait_begin_stream,
.wait_end = zxsio_wait_end_stream,
.unwrap = zxsio_unwrap_stream,
.posix_ioctl = zxsio_posix_ioctl_stream,
.get_vmo = fdio_default_get_vmo,
.get_token = fdio_default_get_token,
.get_attr = fdio_default_get_attr,
.set_attr = fdio_default_set_attr,
.sync = fdio_default_sync,
.readdir = fdio_default_readdir,
.rewind = fdio_default_rewind,
.unlink = fdio_default_unlink,
.truncate = fdio_default_truncate,
.rename = fdio_default_rename,
.link = fdio_default_link,
.get_flags = fdio_default_get_flags,
.set_flags = fdio_default_set_flags,
.recvfrom = zxsio_recvfrom,
.sendto = zxsio_sendto,
.recvmsg = zxsio_recvmsg_stream,
.sendmsg = zxsio_sendmsg_stream,
.shutdown = fdio_socket_shutdown,
};
static fdio_ops_t fdio_socket_dgram_ops = {
.read = zxsio_read_dgram,
.read_at = fdio_default_read_at,
.write = zxsio_write_dgram,
.write_at = fdio_default_write_at,
.seek = fdio_default_seek,
.misc = zxsio_misc,
.close = zxsio_close,
.open = fdio_default_open,
.clone = fdio_default_clone,
.ioctl = zxsio_ioctl,
.wait_begin = zxsio_wait_begin_dgram,
.wait_end = zxsio_wait_end_dgram,
.unwrap = fdio_default_unwrap,
.posix_ioctl = fdio_default_posix_ioctl, // not supported
.get_vmo = fdio_default_get_vmo,
.get_token = fdio_default_get_token,
.get_attr = fdio_default_get_attr,
.set_attr = fdio_default_set_attr,
.sync = fdio_default_sync,
.readdir = fdio_default_readdir,
.rewind = fdio_default_rewind,
.unlink = fdio_default_unlink,
.truncate = fdio_default_truncate,
.rename = fdio_default_rename,
.link = fdio_default_link,
.get_flags = fdio_default_get_flags,
.set_flags = fdio_default_set_flags,
.recvfrom = zxsio_recvfrom,
.sendto = zxsio_sendto,
.recvmsg = zxsio_recvmsg_dgram,
.sendmsg = zxsio_sendmsg_dgram,
.shutdown = fdio_socket_shutdown,
};
static fdio_t* fdio_socket_create(zx_handle_t s, int flags, fdio_ops_t* ops) {
zxsio_t* sio = calloc(1, sizeof(*sio));
if (sio == NULL) {
zx_handle_close(s);
return NULL;
}
sio->io.ops = ops;
sio->io.magic = FDIO_MAGIC;
sio->io.refcount = 1;
sio->io.ioflag = IOFLAG_SOCKET | flags;
sio->s = s;
sio->flags = 0;
return &sio->io;
}
fdio_t* fdio_socket_create_stream(zx_handle_t s, int flags) {
return fdio_socket_create(s, flags, &fdio_socket_stream_ops);
}
fdio_t* fdio_socket_create_datagram(zx_handle_t s, int flags) {
return fdio_socket_create(s, flags, &fdio_socket_dgram_ops);
}