/*
* Copyright (c) 2018-2020 Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include "htclow_socket_discovery_manager.hpp"
#include "htclow_socket_discovery_util.hpp"
namespace ams::htclow::driver {
namespace {
constexpr inline u32 BeaconQueryServiceId = 0xB48F5C51;
}
void SocketDiscoveryManager::OnDriverOpen() {
/* Create our socket. */
m_socket = socket::SocketExempt(socket::Family::Af_Inet, socket::Type::Sock_Dgram, socket::Protocol::IpProto_Udp);
AMS_ABORT_UNLESS(m_socket != -1);
/* Mark driver open. */
m_driver_closed = false;
/* Create our thread. */
R_ABORT_UNLESS(os::CreateThread(std::addressof(m_discovery_thread), ThreadEntry, this, m_thread_stack, os::MemoryPageSize, AMS_GET_SYSTEM_THREAD_PRIORITY(htc, HtclowDiscovery)));
/* Set our thread name. */
os::SetThreadNamePointer(std::addressof(m_discovery_thread), AMS_GET_SYSTEM_THREAD_NAME(htc, HtclowDiscovery));
/* Start our thread. */
os::StartThread(std::addressof(m_discovery_thread));
}
void SocketDiscoveryManager::OnDriverClose() {
/* Mark driver closed. */
m_driver_closed = true;
/* Shutdown our socket. */
socket::Shutdown(m_socket, socket::ShutdownMethod::Shut_RdWr);
/* Close our socket. */
socket::Close(m_socket);
/* Destroy our thread. */
os::WaitThread(std::addressof(m_discovery_thread));
os::DestroyThread(std::addressof(m_discovery_thread));
}
void SocketDiscoveryManager::OnSocketAcceptBegin(u16 port) {
/* ... */
}
void SocketDiscoveryManager::OnSocketAcceptEnd() {
/* ... */
}
void SocketDiscoveryManager::ThreadFunc() {
for (this->DoDiscovery(); !m_driver_closed; this->DoDiscovery()) {
/* Check if the driver is closed five times. */
for (size_t i = 0; i < 5; ++i) {
os::SleepThread(TimeSpan::FromSeconds(1));
if (m_driver_closed) {
return;
}
}
}
}
Result SocketDiscoveryManager::DoDiscovery() {
/* Ensure we close our socket if we fail. */
auto socket_guard = SCOPE_GUARD { socket::Close(m_socket); };
/* Create sockaddr for our socket. */
const socket::SockAddrIn sockaddr = {
.sin_len = 0,
.sin_family = socket::Family::Af_Inet,
.sin_port = socket::InetHtons(20181),
.sin_addr = { socket::InetHtonl(0) },
};
/* Bind our socket. */
const auto bind_res = socket::Bind(m_socket, reinterpret_cast(std::addressof(sockaddr)), sizeof(sockaddr));
R_UNLESS(bind_res != 0, htclow::ResultSocketBindError());
/* Loop processing beacon queries. */
while (true) {
/* Receive a tmipc query header. */
TmipcHeader header;
socket::SockAddr recv_sockaddr;
socket::SockLenT recv_sockaddr_len = sizeof(recv_sockaddr);
const auto recv_res = socket::RecvFrom(m_socket, std::addressof(header), sizeof(header), socket::MsgFlag::MsgFlag_None, std::addressof(recv_sockaddr), std::addressof(recv_sockaddr_len));
/* Check that our receive was valid. */
R_UNLESS(recv_res >= 0, htclow::ResultSocketReceiveFromError());
R_UNLESS(recv_sockaddr_len == sizeof(recv_sockaddr), htclow::ResultSocketReceiveFromError());
/* Check we received a packet header. */
if (recv_res != sizeof(header)) {
continue;
}
/* Check that we received a correctly versioned BeaconQuery packet. */
/* NOTE: Nintendo checks this *after* the following receive, but this seems saner. */
if (header.version != TmipcVersion || header.service_id != BeaconQueryServiceId) {
continue;
}
/* Receive the packet body, if there is one. */
char packet_data[0x120];
/* NOTE: Nintendo does not check this... */
if (header.data_len > sizeof(packet_data)) {
continue;
}
if (header.data_len > 0) {
const auto body_res = socket::RecvFrom(m_socket, packet_data, header.data_len, socket::MsgFlag::MsgFlag_None, std::addressof(recv_sockaddr), std::addressof(recv_sockaddr_len));
R_UNLESS(body_res >= 0, htclow::ResultSocketReceiveFromError());
R_UNLESS(recv_sockaddr_len == sizeof(recv_sockaddr), htclow::ResultSocketReceiveFromError());
if (body_res != header.data_len) {
continue;
}
}
/* Make our beacon response packet. */
const auto len = MakeBeaconResponsePacket(packet_data, sizeof(packet_data));
/* Send the beacon response data. */
const auto send_res = socket::SendTo(m_socket, packet_data, len, socket::MsgFlag::MsgFlag_None, std::addressof(recv_sockaddr), sizeof(recv_sockaddr));
R_UNLESS(send_res >= 0, htclow::ResultSocketSendToError());
}
/* This can never happen, as the above loop should be infinite, but completion logic is here for posterity. */
socket_guard.Cancel();
return ResultSuccess();
}
}