Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions core/libs/prx/libkernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,13 @@ if(BUILD_TESTING)
set_tests_properties(guest_socket_pair PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
endif()
if(NOT WIN32)
add_test_executable(guest_socket_rights_tests ${LIBS_INCLUDE_DIR}/tests/GuestSocketRights.cpp)
target_include_directories(guest_socket_rights_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_socket_rights_tests PRIVATE libkernel libc)
add_test(NAME guest_socket_rights COMMAND guest_socket_rights_tests)
set_tests_properties(guest_socket_rights PROPERTIES TIMEOUT 20)
endif()
add_test_executable(guest_kernel_app_archive_tests ${LIBS_INCLUDE_DIR}/tests/GuestKernelAppArchive.cpp)
target_include_directories(guest_kernel_app_archive_tests PRIVATE ${LIBS_INCLUDE_DIR})
target_link_libraries(guest_kernel_app_archive_tests PRIVATE libkernel libc zarchive)
Expand Down
171 changes: 168 additions & 3 deletions core/libs/prx/libkernel/Socket/src/Udp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "prx/libkernel/Socket/include/SocketError.hpp"
#include "prx/libkernel/Socket/include/SocketOptions.hpp"
#include "prx/libkernel/Socket/include/SocketRuntime.hpp"
#include "prx/libkernel/File/include/ArchivedFile.hpp"
#include "prx/libkernel/File/include/DescriptorControl.hpp"
#include "prx/libkernel/Equeue/Equeue.hpp"
#include "prx/libkernel/File/include/File.hpp"
Expand Down Expand Up @@ -1236,10 +1237,109 @@ static std::int64_t MessageLength(const GuestMsghdr* message) {
}
return static_cast<std::int64_t>(total);
}
constexpr int GuestControlCloseOnExec = 0x40000;
#ifndef _WIN32
constexpr int GuestSocketLevel = 0xffff;
constexpr int GuestRights = 1;
constexpr int GuestControlTruncated = 0x20;
constexpr std::size_t GuestControlHeader = 16;
constexpr std::size_t MostPassedDescriptors = 253;
std::size_t GuestControlSpace(std::size_t bytes) {
return GuestControlHeader + ((bytes + 7u) & ~std::size_t{7});
}
int PassedDescriptors(const GuestMsghdr& message, std::vector<int>& descriptors) {
const auto* control = static_cast<const unsigned char*>(message.control);
std::size_t left = message.controlLength;
std::size_t at = 0;
while (left != 0) {
std::uint32_t length = 0;
int level = 0;
int type = 0;
if (left < 12) return 22;
std::memcpy(&length, control + at, sizeof(length));
std::memcpy(&level, control + at + 4, sizeof(level));
std::memcpy(&type, control + at + 8, sizeof(type));
if (level != GuestSocketLevel || length > left || length < GuestControlHeader) return 22;
if (type != GuestRights) throw std::runtime_error("sendmsg: control message type " + std::to_string(type) + " is not implemented");
const std::size_t count = (length - GuestControlHeader) / sizeof(int);
for (std::size_t index = 0; index < count; ++index) {
int passed = -1;
std::memcpy(&passed, control + at + GuestControlHeader + index * sizeof(int), sizeof(passed));
if (passed < 0 || !DescriptorIsOpen_nid_no_patch(passed)) return 9;
File::RefuseArchivedFile(passed, "sendmsg");
descriptors.push_back(passed);
}
const auto space = GuestControlSpace(length - GuestControlHeader);
if (space >= left) break;
at += space;
left -= space;
}
return 0;
}
std::int64_t SendPassing(Socket& socket, const GuestMsghdr& message, std::vector<char>& data, int flags) {
std::vector<int> descriptors;
if (const int error = PassedDescriptors(message, descriptors)) return Fail(error);
if ((flags & ~GuestSendFlags) != 0) return Fail(45);
if (message.name) return Fail(47);
if (const int waited = WaitForTransfer(socket, true, flags)) return Fail(waited);
iovec vector{data.data(), data.size()};
msghdr host{};
host.msg_iov = &vector;
host.msg_iovlen = 1;
std::vector<char> control(CMSG_SPACE(descriptors.size() * sizeof(int)));
if (!descriptors.empty()) {
host.msg_control = control.data();
host.msg_controllen = control.size();
auto* header = CMSG_FIRSTHDR(&host);
header->cmsg_level = SOL_SOCKET;
header->cmsg_type = SCM_RIGHTS;
header->cmsg_len = CMSG_LEN(descriptors.size() * sizeof(int));
std::memcpy(CMSG_DATA(header), descriptors.data(), descriptors.size() * sizeof(int));
}
const auto result = ::sendmsg(socket.value, &host, SocketSendFlags(socket, flags));
return result < 0 ? Fail(SendError(socket.type)) : result;
}
int GuestFromHostSocket(int domain) {
return domain == AF_UNIX ? GuestUnix : domain == AF_INET ? 2 : domain == AF_INET6 ? 28 : -1;
}
int GuestFromHostSocketType(int type) {
return type == SOCK_STREAM ? 1 : type == SOCK_DGRAM ? 2 : type == SOCK_SEQPACKET ? 5 : -1;
}
int InstallPassed(int native, bool closeOnExec) {
struct stat status{};
if (::fstat(native, &status) != 0) throw std::runtime_error("recvmsg: a received descriptor cannot be queried, errno " + std::to_string(errno));
if (!S_ISSOCK(status.st_mode)) return native;
int domain = 0;
int type = 0;
socklen_t length = sizeof(domain);
if (::getsockopt(native, SOL_SOCKET, SO_DOMAIN, &domain, &length) != 0 || ::getsockopt(native, SOL_SOCKET, SO_TYPE, &type, &length) != 0)
throw std::runtime_error("recvmsg: a received socket cannot be queried, errno " + std::to_string(errno));
const int family = GuestFromHostSocket(domain);
const int guestType = GuestFromHostSocketType(type);
if (family < 0 || guestType < 0) throw std::runtime_error("recvmsg: a received socket of host domain " + std::to_string(domain) + " and type " + std::to_string(type) + " is not supported");
const int number = Install(std::make_shared<Socket>(native, family, guestType));
if (number < 0) return -1;
if (closeOnExec) {
std::lock_guard lock(socketsMutex);
closeOnExecDescriptors.insert(number);
}
return number;
}
#endif
std::int64_t APS5_VABI sendmsg_nid_postfix(int descriptor, const GuestMsghdr* message, int flags) {
const auto total = MessageLength(message);
if (total < 0) return -1;
if (message->control && message->controlLength) throw std::runtime_error("sendmsg: control data is not supported");
const bool control = message->control && message->controlLength;
#ifdef _WIN32
if (control) throw std::runtime_error("sendmsg: control data is not supported on Windows");
#else
std::shared_ptr<Socket> passing;
if (control) {
passing = Lookup(descriptor);
if (!passing) return -1;
if (passing->family != GuestUnix) throw std::runtime_error("sendmsg: control data on an IP socket is not implemented");
}
#endif
std::vector<char> buffer;
try {
buffer.resize(static_cast<std::size_t>(total));
Expand All @@ -1251,14 +1351,24 @@ std::int64_t APS5_VABI sendmsg_nid_postfix(int descriptor, const GuestMsghdr* me
if (message->iov[i].length) std::memcpy(buffer.data() + offset, message->iov[i].base, message->iov[i].length);
offset += message->iov[i].length;
}
#ifndef _WIN32
if (passing) return SendPassing(*passing, *message, buffer, flags);
#endif
return sendto_nid_postfix(descriptor, buffer.data(), buffer.size(), flags, message->name, message->name ? message->nameLength : 0u);
}
std::int64_t APS5_VABI recvmsg_nid_postfix(int descriptor, GuestMsghdr* message, int flags) {
const auto total = MessageLength(message);
if (total < 0) return -1;
const auto socket = Lookup(descriptor);
if (!socket) return -1;
if ((flags & ~GuestReceiveFlags) != 0) return Fail(45);
#ifdef _WIN32
const bool passing = false;
#else
const bool passing = socket->family == GuestUnix && message->control && message->controlLength;
const std::size_t controlCapacity = message->controlLength;
if (passing && (flags & GuestPeek) != 0) throw std::runtime_error("recvmsg: MSG_PEEK with a control buffer on a Unix socket is not implemented");
#endif
if ((flags & ~(GuestReceiveFlags | (passing ? GuestControlCloseOnExec : 0))) != 0) return Fail(45);
const bool records = socket->type == 2 || socket->type == 5;
std::vector<char> buffer;
try {
Expand All @@ -1269,7 +1379,46 @@ std::int64_t APS5_VABI recvmsg_nid_postfix(int descriptor, GuestMsghdr* message,
sockaddr_storage native{};
socklen_t size = sizeof(native);
if (const int waited = WaitForTransfer(*socket, false, flags)) return Fail(waited);
std::int64_t received = ::recvfrom(socket->value, buffer.data(), static_cast<int>(buffer.size()),
std::int64_t received = 0;
#ifndef _WIN32
std::vector<int> passed;
bool controlTruncated = false;
if (passing) {
std::vector<char> control(CMSG_SPACE(MostPassedDescriptors * sizeof(int)));
iovec vector{buffer.data(), buffer.size()};
msghdr host{};
host.msg_name = &native;
host.msg_namelen = size;
host.msg_iov = &vector;
host.msg_iovlen = 1;
host.msg_control = control.data();
host.msg_controllen = control.size();
received = ::recvmsg(socket->value, &host, NativeReceiveFlags(flags) | ((flags & GuestControlCloseOnExec) != 0 ? MSG_CMSG_CLOEXEC : 0));
if (received < 0) return Fail(NativeError());
size = host.msg_namelen;
controlTruncated = (host.msg_flags & MSG_CTRUNC) != 0;
for (auto* header = CMSG_FIRSTHDR(&host); header; header = CMSG_NXTHDR(&host, header)) {
if (header->cmsg_level != SOL_SOCKET || header->cmsg_type != SCM_RIGHTS) continue;
const auto count = (header->cmsg_len - CMSG_LEN(0)) / sizeof(int);
for (std::size_t index = 0; index < count; ++index) {
int handed = -1;
std::memcpy(&handed, CMSG_DATA(header) + index * sizeof(int), sizeof(handed));
passed.push_back(handed);
}
}
for (std::size_t index = 0; index < passed.size(); ++index) {
const int number = InstallPassed(passed[index], (flags & GuestControlCloseOnExec) != 0);
if (number < 0) {
const int error = *__error_nid_postfix();
for (std::size_t rest = index; rest < passed.size(); ++rest) ::close(passed[rest]);
passed.resize(index);
return Fail(error);
}
passed[index] = number;
}
} else
#endif
received = ::recvfrom(socket->value, buffer.data(), static_cast<int>(buffer.size()),
NativeReceiveFlags(flags), reinterpret_cast<sockaddr*>(&native), &size);
if (received < 0) {
#ifdef _WIN32
Expand Down Expand Up @@ -1300,6 +1449,22 @@ std::int64_t APS5_VABI recvmsg_nid_postfix(int descriptor, GuestMsghdr* message,
}
}
message->controlLength = 0;
#ifndef _WIN32
if (passing && !passed.empty()) {
const auto bytes = passed.size() * sizeof(int);
std::vector<unsigned char> record(GuestControlSpace(bytes), 0);
const auto length = static_cast<std::uint32_t>(GuestControlHeader + bytes);
std::memcpy(record.data(), &length, sizeof(length));
std::memcpy(record.data() + 4, &GuestSocketLevel, sizeof(GuestSocketLevel));
std::memcpy(record.data() + 8, &GuestRights, sizeof(GuestRights));
std::memcpy(record.data() + GuestControlHeader, passed.data(), bytes);
const auto copied = std::min<std::size_t>(record.size(), controlCapacity);
std::memcpy(message->control, record.data(), copied);
message->controlLength = static_cast<std::uint32_t>(copied);
if (copied < record.size()) controlTruncated = true;
}
if (controlTruncated) message->flags |= GuestControlTruncated;
#endif
return received;
}
struct GuestSendfileHeaders {
Expand Down
Loading
Loading