| // Copyright 2018 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 <fidl/fuchsia.net.name/cpp/wire.h> |
| #include <fidl/fuchsia.net/cpp/wire.h> |
| #include <fidl/fuchsia.posix.socket.packet/cpp/wire.h> |
| #include <fidl/fuchsia.posix.socket.raw/cpp/wire.h> |
| #include <ifaddrs.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/eventpair.h> |
| #include <lib/zxio/bsdsocket.h> |
| #include <lib/zxio/ops.h> |
| #include <lib/zxio/types.h> |
| #include <lib/zxio/watcher.h> |
| #include <lib/zxio/zxio.h> |
| #include <string.h> |
| #include <zircon/syscalls.h> |
| |
| #include <atomic> |
| #include <new> |
| #include <type_traits> |
| |
| #include <netpacket/packet.h> |
| |
| #include "private.h" |
| |
| namespace fsocket = fuchsia_posix_socket; |
| namespace frawsocket = fuchsia_posix_socket_raw; |
| namespace fpacketsocket = fuchsia_posix_socket_packet; |
| |
| // The private fields of a |zxio_t| object. |
| // |
| // In |ops.h|, the |zxio_t| struct is defined as opaque. Clients of the zxio |
| // library are forbidden from relying upon the structure of |zxio_t| objects. |
| // To avoid temptation, the details of the structure are defined only in this |
| // implementation file and are not visible in the header. |
| using zxio_internal_t = struct zxio_internal { |
| explicit zxio_internal(const zxio_ops_t* ops) : ops(ops) {} |
| |
| const zxio_ops_t* ops; |
| |
| // When adding fields to the |zxio_internal_t| struct, one may take from |
| // the reserved bytes here to ensure that the ABI stays compatible. |
| uint64_t reserved[3]; |
| }; |
| |
| static_assert(sizeof(zxio_t) == sizeof(zxio_internal_t), "zxio_t should match zxio_internal_t"); |
| |
| // Converters from the public (opaque) types to the internal (implementation) types. |
| namespace { |
| |
| zxio_internal_t* to_internal(zxio_t* io) { return reinterpret_cast<zxio_internal_t*>(io); } |
| |
| const zxio_internal_t* to_internal(const zxio_t* io) { |
| return reinterpret_cast<const zxio_internal_t*>(io); |
| } |
| |
| } // namespace |
| |
| bool zxio_is_valid(const zxio_t* io) { |
| if (io == nullptr) { |
| return false; |
| } |
| const zxio_internal_t* zio = to_internal(io); |
| return zio->ops != nullptr; |
| } |
| |
| void zxio_init(zxio_t* io, const zxio_ops_t* ops) { new (io) zxio_internal_t(ops); } |
| |
| const zxio_ops_t* zxio_get_ops(zxio_t* io) { |
| const zxio_internal_t* zio = to_internal(io); |
| return zio->ops; |
| } |
| |
| zx_status_t zxio_close(zxio_t* io, const bool should_wait) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| static_assert(std::is_trivially_destructible<zxio_internal_t>::value, |
| "zxio_internal_t must have trivial destructor"); |
| zxio_internal_t* zio = to_internal(io); |
| zx_status_t status = zio->ops->close(io, should_wait); |
| // Poison the object. Double destruction is undefined behavior. |
| zio->ops = nullptr; |
| return status; |
| } |
| |
| zx_status_t zxio_release(zxio_t* io, zx_handle_t* out_handle) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->release(io, out_handle); |
| } |
| |
| zx_status_t zxio_borrow(zxio_t* io, zx_handle_t* out_handle) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->borrow(io, out_handle); |
| } |
| |
| zx_status_t zxio_clone(zxio_t* io, zx_handle_t* out_handle) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->clone(io, out_handle); |
| } |
| |
| zx_status_t zxio_wait_one(zxio_t* io, zxio_signals_t signals, zx_time_t deadline, |
| zxio_signals_t* out_observed) { |
| if (!zxio_is_valid(io)) { |
| *out_observed = ZXIO_SIGNAL_NONE; |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zx_handle_t handle = ZX_HANDLE_INVALID; |
| zx_signals_t zx_signals = ZX_SIGNAL_NONE; |
| zxio_wait_begin(io, signals, &handle, &zx_signals); |
| if (handle == ZX_HANDLE_INVALID) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| zx_signals_t zx_observed = ZX_SIGNAL_NONE; |
| zx_status_t status = zx_object_wait_one(handle, zx_signals, deadline, &zx_observed); |
| if (status != ZX_OK) { |
| return status; |
| } |
| zxio_wait_end(io, zx_signals, out_observed); |
| return ZX_OK; |
| } |
| |
| void zxio_wait_begin(zxio_t* io, zxio_signals_t zxio_signals, zx_handle_t* out_handle, |
| zx_signals_t* out_zx_signals) { |
| if (!zxio_is_valid(io)) { |
| *out_handle = ZX_HANDLE_INVALID; |
| *out_zx_signals = ZX_SIGNAL_NONE; |
| return; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->wait_begin(io, zxio_signals, out_handle, out_zx_signals); |
| } |
| |
| void zxio_wait_end(zxio_t* io, zx_signals_t zx_signals, zxio_signals_t* out_zxio_signals) { |
| if (!zxio_is_valid(io)) { |
| *out_zxio_signals = ZXIO_SIGNAL_NONE; |
| return; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->wait_end(io, zx_signals, out_zxio_signals); |
| } |
| |
| zx_status_t zxio_sync(zxio_t* io) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->sync(io); |
| } |
| |
| zx_status_t zxio_attr_get(zxio_t* io, zxio_node_attributes_t* inout_attr) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| if (inout_attr->has.fsverity_root_hash && inout_attr->fsverity_root_hash == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->attr_get(io, inout_attr); |
| } |
| |
| zx_status_t zxio_attr_set(zxio_t* io, const zxio_node_attributes_t* attr) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->attr_set(io, attr); |
| } |
| |
| zx_status_t zxio_enable_verity(zxio_t* io, const zxio_fsverity_descriptor_t* descriptor) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->enable_verity(io, descriptor); |
| } |
| |
| zx_status_t zxio_read(zxio_t* io, void* buffer, size_t capacity, zxio_flags_t flags, |
| size_t* out_actual) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| const zx_iovec_t vector = { |
| .buffer = buffer, |
| .capacity = capacity, |
| }; |
| return zxio_readv(io, &vector, 1, flags, out_actual); |
| } |
| |
| zx_status_t zxio_read_at(zxio_t* io, zx_off_t offset, void* buffer, size_t capacity, |
| zxio_flags_t flags, size_t* out_actual) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| const zx_iovec_t vector = { |
| .buffer = buffer, |
| .capacity = capacity, |
| }; |
| return zxio_readv_at(io, offset, &vector, 1, flags, out_actual); |
| } |
| |
| zx_status_t zxio_write(zxio_t* io, const void* buffer, size_t capacity, zxio_flags_t flags, |
| size_t* out_actual) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| const zx_iovec_t vector = { |
| .buffer = const_cast<void*>(buffer), |
| .capacity = capacity, |
| }; |
| return zxio_writev(io, &vector, 1, flags, out_actual); |
| } |
| |
| zx_status_t zxio_write_at(zxio_t* io, zx_off_t offset, const void* buffer, size_t capacity, |
| zxio_flags_t flags, size_t* out_actual) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| const zx_iovec_t vector = { |
| .buffer = const_cast<void*>(buffer), |
| .capacity = capacity, |
| }; |
| return zxio_writev_at(io, offset, &vector, 1, flags, out_actual); |
| } |
| |
| zx_status_t zxio_readv(zxio_t* io, const zx_iovec_t* vector, size_t vector_count, |
| zxio_flags_t flags, size_t* out_actual) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->readv(io, vector, vector_count, flags, out_actual); |
| } |
| |
| zx_status_t zxio_readv_at(zxio_t* io, zx_off_t offset, const zx_iovec_t* vector, |
| size_t vector_count, zxio_flags_t flags, size_t* out_actual) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->readv_at(io, offset, vector, vector_count, flags, out_actual); |
| } |
| |
| zx_status_t zxio_writev(zxio_t* io, const zx_iovec_t* vector, size_t vector_count, |
| zxio_flags_t flags, size_t* out_actual) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->writev(io, vector, vector_count, flags, out_actual); |
| } |
| |
| zx_status_t zxio_writev_at(zxio_t* io, zx_off_t offset, const zx_iovec_t* vector, |
| size_t vector_count, zxio_flags_t flags, size_t* out_actual) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->writev_at(io, offset, vector, vector_count, flags, out_actual); |
| } |
| |
| static_assert(ZX_STREAM_SEEK_ORIGIN_START == ZXIO_SEEK_ORIGIN_START, "ZXIO should match ZX"); |
| static_assert(ZX_STREAM_SEEK_ORIGIN_CURRENT == ZXIO_SEEK_ORIGIN_CURRENT, "ZXIO should match ZX"); |
| static_assert(ZX_STREAM_SEEK_ORIGIN_END == ZXIO_SEEK_ORIGIN_END, "ZXIO should match ZX"); |
| |
| zx_status_t zxio_seek(zxio_t* io, zxio_seek_origin_t start, int64_t offset, size_t* out_offset) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->seek(io, start, offset, out_offset); |
| } |
| |
| zx_status_t zxio_truncate(zxio_t* io, uint64_t length) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->truncate(io, length); |
| } |
| |
| zx_status_t zxio_flags_get(zxio_t* io, uint32_t* out_flags) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->flags_get(io, out_flags); |
| } |
| |
| zx_status_t zxio_flags_set(zxio_t* io, uint32_t flags) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->flags_set(io, flags); |
| } |
| |
| zx_status_t zxio_token_get(zxio_t* io, zx_handle_t* out_token) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->token_get(io, out_token); |
| } |
| |
| zx_status_t zxio_vmo_get(zxio_t* io, zxio_vmo_flags_t flags, zx_handle_t* out_vmo) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->vmo_get(io, flags, out_vmo); |
| } |
| |
| zx_status_t zxio_on_mapped(zxio_t* io, void* ptr) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->on_mapped(io, ptr); |
| } |
| |
| zx_status_t zxio_get_read_buffer_available(zxio_t* io, size_t* out_available) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->get_read_buffer_available(io, out_available); |
| } |
| |
| zx_status_t zxio_shutdown(zxio_t* io, zxio_shutdown_options_t options, int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->shutdown(io, options, out_code); |
| } |
| |
| zx_status_t zxio_open(zxio_t* directory, uint32_t flags, const char* path, size_t path_len, |
| zxio_storage_t* storage) { |
| if (!zxio_is_valid(directory)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(directory); |
| return zio->ops->open(directory, flags, path, path_len, storage); |
| } |
| |
| zx_status_t zxio_open3(zxio_t* directory, const char* path, size_t path_len, |
| zxio_open_flags_t flags, const zxio_open_options_t* options, |
| zxio_storage_t* storage) { |
| if (!zxio_is_valid(directory)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(directory); |
| return zio->ops->open3(directory, path, path_len, flags, options, storage); |
| } |
| |
| zx_status_t zxio_open_async(zxio_t* directory, uint32_t flags, const char* path, size_t path_len, |
| zx_handle_t request) { |
| if (!zxio_is_valid(directory)) { |
| zx_handle_close(request); |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(directory); |
| return zio->ops->open_async(directory, flags, path, path_len, request); |
| } |
| |
| zx_status_t zxio_unlink(zxio_t* directory, const char* name, size_t name_len, int flags) { |
| if (!zxio_is_valid(directory)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(directory); |
| return zio->ops->unlink(directory, name, name_len, flags); |
| } |
| |
| zx_status_t zxio_rename(zxio_t* old_directory, const char* old_path, size_t old_path_len, |
| zx_handle_t new_directory_token, const char* new_path, |
| size_t new_path_len) { |
| if (!zxio_is_valid(old_directory)) { |
| zx_handle_close(new_directory_token); |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(old_directory); |
| return zio->ops->rename(old_directory, old_path, old_path_len, new_directory_token, new_path, |
| new_path_len); |
| } |
| |
| zx_status_t zxio_link(zxio_t* src_directory, const char* src_path, size_t src_path_len, |
| zx_handle_t dst_directory_token, const char* dst_path, size_t dst_path_len) { |
| if (!zxio_is_valid(src_directory)) { |
| zx_handle_close(dst_directory_token); |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(src_directory); |
| return zio->ops->link(src_directory, src_path, src_path_len, dst_directory_token, dst_path, |
| dst_path_len); |
| } |
| |
| zx_status_t zxio_link_into(zxio_t* object, zx_handle_t dst_directory_token, const char* dst_path, |
| size_t dst_path_len) { |
| if (!zxio_is_valid(object)) { |
| zx_handle_close(dst_directory_token); |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(object); |
| return zio->ops->link_into(object, dst_directory_token, dst_path, dst_path_len); |
| } |
| |
| zx_status_t zxio_dirent_iterator_init(zxio_dirent_iterator_t* iterator, zxio_t* directory) { |
| if (!zxio_is_valid(directory)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| if (iterator == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| zxio_internal_t* zio = to_internal(directory); |
| return zio->ops->dirent_iterator_init(directory, iterator); |
| } |
| |
| zx_status_t zxio_dirent_iterator_next(zxio_dirent_iterator_t* iterator, |
| zxio_dirent_t* inout_entry) { |
| if (!zxio_is_valid(iterator->io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| if (inout_entry == nullptr || inout_entry->name == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| zxio_internal_t* zio = to_internal(iterator->io); |
| return zio->ops->dirent_iterator_next(iterator->io, iterator, inout_entry); |
| } |
| |
| zx_status_t zxio_dirent_iterator_rewind(zxio_dirent_iterator_t* iterator) { |
| if (!zxio_is_valid(iterator->io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(iterator->io); |
| return zio->ops->dirent_iterator_rewind(iterator->io, iterator); |
| } |
| |
| void zxio_dirent_iterator_destroy(zxio_dirent_iterator_t* iterator) { |
| if (!zxio_is_valid(iterator->io)) { |
| return; |
| } |
| zxio_internal_t* zio = to_internal(iterator->io); |
| zio->ops->dirent_iterator_destroy(iterator->io, iterator); |
| } |
| |
| zx_status_t zxio_isatty(zxio_t* io, bool* tty) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->isatty(io, tty); |
| } |
| |
| zx_status_t zxio_get_window_size(zxio_t* io, uint32_t* width, uint32_t* height) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| if (width == nullptr || height == nullptr) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->get_window_size(io, width, height); |
| } |
| |
| zx_status_t zxio_set_window_size(zxio_t* io, uint32_t width, uint32_t height) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->set_window_size(io, width, height); |
| } |
| |
| zx_status_t zxio_ioctl(zxio_t* io, int request, int16_t* out_code, va_list va) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->ioctl(io, request, out_code, va); |
| } |
| |
| zx_status_t zxio_watch_directory(zxio_t* directory, zxio_watch_directory_cb cb, zx_time_t deadline, |
| void* context) { |
| if (!zxio_is_valid(directory)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(directory); |
| return zio->ops->watch_directory(directory, cb, deadline, context); |
| } |
| |
| zx_status_t zxio_bind(zxio_t* io, const struct sockaddr* addr, socklen_t addrlen, |
| int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->bind(io, addr, addrlen, out_code); |
| } |
| |
| zx_status_t zxio_connect(zxio_t* io, const struct sockaddr* addr, socklen_t addrlen, |
| int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->connect(io, addr, addrlen, out_code); |
| } |
| |
| zx_status_t zxio_listen(zxio_t* io, int backlog, int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->listen(io, backlog, out_code); |
| } |
| |
| zx_status_t zxio_accept(zxio_t* io, struct sockaddr* addr, socklen_t* addrlen, |
| zxio_storage_t* out_storage, int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->accept(io, addr, addrlen, out_storage, out_code); |
| } |
| |
| zx_status_t zxio_getsockname(zxio_t* io, struct sockaddr* addr, socklen_t* addrlen, |
| int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->getsockname(io, addr, addrlen, out_code); |
| } |
| |
| zx_status_t zxio_getpeername(zxio_t* io, struct sockaddr* addr, socklen_t* addrlen, |
| int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->getpeername(io, addr, addrlen, out_code); |
| } |
| |
| zx_status_t zxio_getsockopt(zxio_t* io, int level, int optname, void* optval, socklen_t* optlen, |
| int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->getsockopt(io, level, optname, optval, optlen, out_code); |
| } |
| |
| zx_status_t zxio_setsockopt(zxio_t* io, int level, int optname, const void* optval, |
| socklen_t optlen, int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->setsockopt(io, level, optname, optval, optlen, out_code); |
| } |
| |
| zx_status_t zxio_recvmsg(zxio_t* io, struct msghdr* msg, int flags, size_t* out_actual, |
| int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->recvmsg(io, msg, flags, out_actual, out_code); |
| } |
| |
| zx_status_t zxio_sendmsg(zxio_t* io, const struct msghdr* msg, int flags, size_t* out_actual, |
| int16_t* out_code) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->sendmsg(io, msg, flags, out_actual, out_code); |
| } |
| |
| template <typename T> |
| zx::result<fidl::UnownedClientEnd<T>> connect_socket_provider( |
| zxio_service_connector service_connector) { |
| zx_handle_t socket_provider_handle; |
| zx_status_t status = |
| service_connector(fidl::DiscoverableProtocolName<T>, &socket_provider_handle); |
| if (status != ZX_OK) { |
| return zx::error(status); |
| } |
| return zx::ok(fidl::UnownedClientEnd<T>(zx::unowned_channel(socket_provider_handle))); |
| } |
| |
| zx_status_t zxio_socket(zxio_service_connector service_connector, int domain, int type, |
| int protocol, zxio_storage_alloc allocator, void** out_context, |
| int16_t* out_code) { |
| zxio_storage_t* zxio_storage = nullptr; |
| fsocket::wire::Domain sock_domain; |
| switch (domain) { |
| case AF_PACKET: { |
| if ((protocol > std::numeric_limits<uint16_t>::max()) || |
| (protocol < std::numeric_limits<uint16_t>::min())) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| fpacketsocket::wire::Kind kind; |
| switch (type) { |
| case SOCK_DGRAM: |
| kind = fpacketsocket::wire::Kind::kNetwork; |
| break; |
| case SOCK_RAW: |
| kind = fpacketsocket::wire::Kind::kLink; |
| break; |
| default: |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx::result<fidl::UnownedClientEnd<fpacketsocket::Provider>> provider = |
| connect_socket_provider<fpacketsocket::Provider>(service_connector); |
| if (provider.is_error()) { |
| return ZX_ERR_IO; |
| } |
| fidl::WireResult socket_result = fidl::WireCall(provider.value())->Socket(kind); |
| if (!socket_result.ok()) { |
| return socket_result.status(); |
| } |
| if (socket_result->is_error()) { |
| *out_code = static_cast<int16_t>(socket_result->error_value()); |
| return ZX_OK; |
| } |
| fidl::ClientEnd<fpacketsocket::Socket>& control = socket_result->value()->socket; |
| fidl::WireResult result = fidl::WireCall(control)->Describe(); |
| if (!result.ok()) { |
| return result.status(); |
| } |
| fidl::WireResponse response = result.value(); |
| if (!response.has_event()) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (zx_status_t status = |
| allocator(ZXIO_OBJECT_TYPE_PACKET_SOCKET, &zxio_storage, out_context); |
| status != ZX_OK || zxio_storage == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| if (zx_status_t status = zxio_packet_socket_init(zxio_storage, std::move(response.event()), |
| std::move(control)); |
| status != ZX_OK) { |
| return status; |
| } |
| const sockaddr_ll sll = { |
| .sll_family = AF_PACKET, |
| // NB: protocol is in network byte order. |
| .sll_protocol = static_cast<uint16_t>(protocol), |
| }; |
| |
| if (sll.sll_protocol != 0) { |
| // We successfully created the packet socket but the caller wants the |
| // socket to be associated with some protocol so we do that now. |
| if (zx_status_t status = zxio_bind( |
| &zxio_storage->io, reinterpret_cast<const sockaddr*>(&sll), sizeof(sll), out_code); |
| status != ZX_OK) { |
| return status; |
| } |
| } |
| *out_code = 0; |
| return ZX_OK; |
| } |
| case AF_INET: |
| sock_domain = fsocket::wire::Domain::kIpv4; |
| break; |
| case AF_INET6: |
| sock_domain = fsocket::wire::Domain::kIpv6; |
| break; |
| default: |
| return ZX_ERR_PROTOCOL_NOT_SUPPORTED; |
| } |
| |
| switch (type) { |
| case SOCK_STREAM: |
| switch (protocol) { |
| case IPPROTO_IP: |
| case IPPROTO_TCP: { |
| zx::result<fidl::UnownedClientEnd<fsocket::Provider>> provider = |
| connect_socket_provider<fsocket::Provider>(service_connector); |
| if (provider.is_error()) { |
| return ZX_ERR_IO; |
| } |
| auto socket_result = |
| fidl::WireCall(provider.value()) |
| ->StreamSocket(sock_domain, fsocket::wire::StreamSocketProtocol::kTcp); |
| if (socket_result.status() != ZX_OK) { |
| return socket_result.status(); |
| } |
| if (socket_result->is_error()) { |
| *out_code = static_cast<int16_t>(socket_result->error_value()); |
| return ZX_OK; |
| } |
| |
| fidl::ClientEnd<fsocket::StreamSocket>& control = socket_result->value()->s; |
| fidl::WireResult result = fidl::WireCall(control)->Describe(); |
| if (!result.ok()) { |
| return result.status(); |
| } |
| fidl::WireResponse response = result.value(); |
| if (!response.has_socket()) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| zx_info_socket_t info; |
| zx::socket& socket = response.socket(); |
| if (zx_status_t status = |
| socket.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr); |
| status != ZX_OK) { |
| return status; |
| } |
| if (zx_status_t status = |
| allocator(ZXIO_OBJECT_TYPE_STREAM_SOCKET, &zxio_storage, out_context); |
| status != ZX_OK || zxio_storage == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| if (zx_status_t status = zxio_stream_socket_init( |
| zxio_storage, std::move(response.socket()), info, false, std::move(control)); |
| status != ZX_OK) { |
| return status; |
| } |
| |
| } break; |
| default: |
| return ZX_ERR_PROTOCOL_NOT_SUPPORTED; |
| } |
| break; |
| case SOCK_DGRAM: { |
| fsocket::wire::DatagramSocketProtocol proto; |
| switch (protocol) { |
| case IPPROTO_IP: |
| case IPPROTO_UDP: |
| proto = fsocket::wire::DatagramSocketProtocol::kUdp; |
| break; |
| case IPPROTO_ICMP: |
| if (sock_domain != fsocket::wire::Domain::kIpv4) { |
| return ZX_ERR_PROTOCOL_NOT_SUPPORTED; |
| } |
| proto = fsocket::wire::DatagramSocketProtocol::kIcmpEcho; |
| break; |
| case IPPROTO_ICMPV6: |
| if (sock_domain != fsocket::wire::Domain::kIpv6) { |
| return ZX_ERR_PROTOCOL_NOT_SUPPORTED; |
| } |
| proto = fsocket::wire::DatagramSocketProtocol::kIcmpEcho; |
| break; |
| default: |
| return ZX_ERR_PROTOCOL_NOT_SUPPORTED; |
| } |
| |
| zx::result<fidl::UnownedClientEnd<fsocket::Provider>> provider = |
| connect_socket_provider<fsocket::Provider>(service_connector); |
| if (provider.is_error()) { |
| return ZX_ERR_IO; |
| } |
| |
| fidl::WireResult socket_result = |
| fidl::WireCall(provider.value())->DatagramSocket(sock_domain, proto); |
| if (socket_result.status() != ZX_OK) { |
| return socket_result.status(); |
| } |
| if (socket_result->is_error()) { |
| *out_code = static_cast<int16_t>(socket_result->error_value()); |
| return ZX_OK; |
| } |
| fsocket::wire::ProviderDatagramSocketResponse& response = *socket_result->value(); |
| if (response.has_invalid_tag()) { |
| return ZX_ERR_IO; |
| } |
| switch (response.Which()) { |
| case fsocket::wire::ProviderDatagramSocketResponse::Tag::kDatagramSocket: { |
| fidl::ClientEnd<fsocket::DatagramSocket>& control = response.datagram_socket(); |
| fidl::WireResult result = fidl::WireCall(control)->Describe(); |
| if (!result.ok()) { |
| return result.status(); |
| } |
| fidl::WireResponse response = result.value(); |
| if (!response.has_socket()) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (!response.has_tx_meta_buf_size()) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (!response.has_rx_meta_buf_size()) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (!(response.has_metadata_encoding_protocol_version() && |
| response.metadata_encoding_protocol_version() == |
| fsocket::UdpMetadataEncodingProtocolVersion::kZero)) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| zx::socket& socket = response.socket(); |
| zx_info_socket_t info; |
| if (zx_status_t status = |
| socket.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr); |
| status != ZX_OK) { |
| return status; |
| } |
| if (zx_status_t status = |
| allocator(ZXIO_OBJECT_TYPE_DATAGRAM_SOCKET, &zxio_storage, out_context); |
| status != ZX_OK || zxio_storage == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| if (zx_status_t status = zxio_datagram_socket_init(zxio_storage, std::move(socket), info, |
| { |
| response.tx_meta_buf_size(), |
| response.rx_meta_buf_size(), |
| }, |
| std::move(control)); |
| status != ZX_OK) { |
| return status; |
| } |
| } break; |
| case fsocket::wire::ProviderDatagramSocketResponse::Tag::kSynchronousDatagramSocket: { |
| fidl::ClientEnd<fsocket::SynchronousDatagramSocket>& control = |
| response.synchronous_datagram_socket(); |
| fidl::WireResult result = fidl::WireCall(control)->Describe(); |
| if (!result.ok()) { |
| return result.status(); |
| } |
| fidl::WireResponse response = result.value(); |
| if (!response.has_event()) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (zx_status_t status = allocator(ZXIO_OBJECT_TYPE_SYNCHRONOUS_DATAGRAM_SOCKET, |
| &zxio_storage, out_context); |
| status != ZX_OK || zxio_storage == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| if (zx_status_t status = zxio_synchronous_datagram_socket_init( |
| zxio_storage, std::move(response.event()), std::move(control)); |
| status != ZX_OK) { |
| return status; |
| } |
| } break; |
| } |
| } break; |
| case SOCK_RAW: { |
| if (protocol == 0) { |
| return ZX_ERR_PROTOCOL_NOT_SUPPORTED; |
| } |
| if ((protocol > std::numeric_limits<uint8_t>::max()) || |
| (protocol < std::numeric_limits<uint8_t>::min())) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| frawsocket::wire::ProtocolAssociation proto_assoc; |
| uint8_t sock_protocol = static_cast<uint8_t>(protocol); |
| // Sockets created with IPPROTO_RAW are only used to send packets as per |
| // https://linux.die.net/man/7/raw, |
| // |
| // A protocol of IPPROTO_RAW implies enabled IP_HDRINCL and is able to |
| // send any IP protocol that is specified in the passed header. Receiving |
| // of all IP protocols via IPPROTO_RAW is not possible using raw sockets. |
| if (protocol == IPPROTO_RAW) { |
| proto_assoc = frawsocket::wire::ProtocolAssociation::WithUnassociated({}); |
| } else { |
| proto_assoc = frawsocket::wire::ProtocolAssociation::WithAssociated(sock_protocol); |
| } |
| |
| zx::result<fidl::UnownedClientEnd<frawsocket::Provider>> provider = |
| connect_socket_provider<frawsocket::Provider>(service_connector); |
| if (provider.is_error()) { |
| return ZX_ERR_IO; |
| } |
| fidl::WireResult socket_result = |
| fidl::WireCall(provider.value())->Socket(sock_domain, proto_assoc); |
| if (!socket_result.ok()) { |
| return ZX_ERR_PEER_CLOSED; |
| } |
| if (socket_result->is_error()) { |
| *out_code = static_cast<int16_t>(socket_result->error_value()); |
| return ZX_OK; |
| } |
| fidl::ClientEnd<frawsocket::Socket>& control = socket_result->value()->s; |
| fidl::WireResult result = fidl::WireCall(control)->Describe(); |
| if (!result.ok()) { |
| return result.status(); |
| } |
| fidl::WireResponse response = result.value(); |
| if (!response.has_event()) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| if (zx_status_t status = allocator(ZXIO_OBJECT_TYPE_RAW_SOCKET, &zxio_storage, out_context); |
| status != ZX_OK || zxio_storage == nullptr) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| if (zx_status_t status = |
| zxio_raw_socket_init(zxio_storage, std::move(response.event()), std::move(control)); |
| status != ZX_OK) { |
| return status; |
| } |
| } break; |
| default: |
| return ZX_ERR_PROTOCOL_NOT_SUPPORTED; |
| } |
| |
| *out_code = 0; |
| return ZX_OK; |
| } |
| |
| zx_status_t zxio_read_link(zxio_t* io, const uint8_t** out_target, size_t* out_target_len) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->read_link(io, out_target, out_target_len); |
| } |
| |
| zx_status_t zxio_create_symlink(zxio_t* io, const char* name, size_t name_len, |
| const uint8_t* target, size_t target_len, zxio_storage_t* storage) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->create_symlink(io, name, name_len, target, target_len, storage); |
| } |
| |
| zx_status_t zxio_xattr_list(zxio_t* io, |
| void (*callback)(void* context, const uint8_t* name, size_t name_len), |
| void* context) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->xattr_list(io, callback, context); |
| } |
| |
| zx_status_t zxio_xattr_get(zxio_t* io, const uint8_t* name, size_t name_len, |
| zx_status_t (*callback)(void* context, zxio_xattr_data_t data), |
| void* context) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->xattr_get(io, name, name_len, callback, context); |
| } |
| |
| zx_status_t zxio_xattr_set(zxio_t* io, const uint8_t* name, size_t name_len, const uint8_t* value, |
| size_t value_len, zxio_xattr_set_mode_t mode) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->xattr_set(io, name, name_len, value, value_len, mode); |
| } |
| |
| zx_status_t zxio_xattr_remove(zxio_t* io, const uint8_t* name, size_t name_len) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->xattr_remove(io, name, name_len); |
| } |
| |
| zx_status_t zxio_allocate(zxio_t* io, uint64_t offset, uint64_t len, zxio_allocate_mode_t mode) { |
| if (!zxio_is_valid(io)) { |
| return ZX_ERR_BAD_HANDLE; |
| } |
| zxio_internal_t* zio = to_internal(io); |
| return zio->ops->allocate(io, offset, len, mode); |
| } |