#include "remoting/host/win/rdp_client.h"
#include <windows.h>
#include <cstdint>
#include <memory>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/numerics/safe_conversions.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_runner.h"
#include "net/base/ip_address.h"
#include "net/base/ip_endpoint.h"
#include "remoting/base/typed_buffer.h"
#include "remoting/host/base/screen_resolution.h"
#include "remoting/host/win/rdp_client_window.h"
namespace remoting {
namespace {
const unsigned char kRdpLoopbackAddress[] = {127, 0, 0, 2};
}
class RdpClient::Core : public base::RefCountedThreadSafe<Core>,
public RdpClientWindow::EventHandler {
public:
Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
RdpClient::EventHandler* event_handler);
Core(const Core&) = delete;
Core& operator=(const Core&) = delete;
void Connect(const ScreenResolution& resolution,
const std::string& terminal_id,
DWORD port_number);
void Disconnect();
void InjectSas();
void ChangeResolution(const ScreenResolution& resolution);
void OnConnected() override;
void OnDisconnected() override;
private:
friend class base::RefCountedThreadSafe<Core>;
~Core() override;
void NotifyConnected();
void NotifyClosed();
scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
raw_ptr<RdpClient::EventHandler> event_handler_;
std::unique_ptr<RdpClientWindow> rdp_client_window_;
scoped_refptr<Core> self_;
};
RdpClient::RdpClient(
scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
const ScreenResolution& resolution,
const std::string& terminal_id,
DWORD port_number,
EventHandler* event_handler) {
DCHECK(caller_task_runner->BelongsToCurrentThread());
core_ = new Core(caller_task_runner, ui_task_runner, event_handler);
core_->Connect(resolution, terminal_id, port_number);
}
RdpClient::~RdpClient() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
core_->Disconnect();
}
void RdpClient::InjectSas() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
core_->InjectSas();
}
void RdpClient::ChangeResolution(const ScreenResolution& resolution) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
core_->ChangeResolution(resolution);
}
RdpClient::Core::Core(
scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
RdpClient::EventHandler* event_handler)
: caller_task_runner_(caller_task_runner),
ui_task_runner_(ui_task_runner),
event_handler_(event_handler) {}
void RdpClient::Core::Connect(const ScreenResolution& resolution,
const std::string& terminal_id,
DWORD port_number) {
if (!ui_task_runner_->BelongsToCurrentThread()) {
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Core::Connect, this, resolution, terminal_id,
port_number));
return;
}
DCHECK(base::CurrentUIThread::IsSet());
DCHECK(!rdp_client_window_);
DCHECK(!self_.get());
net::IPEndPoint server_endpoint(net::IPAddress(kRdpLoopbackAddress),
base::checked_cast<uint16_t>(port_number));
rdp_client_window_ =
std::make_unique<RdpClientWindow>(server_endpoint, terminal_id, this);
if (!rdp_client_window_->Connect(resolution)) {
rdp_client_window_.reset();
NotifyClosed();
}
}
void RdpClient::Core::Disconnect() {
if (!ui_task_runner_->BelongsToCurrentThread()) {
ui_task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Core::Disconnect, this));
return;
}
event_handler_ = nullptr;
if (rdp_client_window_) {
self_ = this;
rdp_client_window_->Disconnect();
}
}
void RdpClient::Core::InjectSas() {
if (!ui_task_runner_->BelongsToCurrentThread()) {
ui_task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Core::InjectSas, this));
return;
}
if (rdp_client_window_) {
rdp_client_window_->InjectSas();
}
}
void RdpClient::Core::ChangeResolution(const ScreenResolution& resolution) {
if (!ui_task_runner_->BelongsToCurrentThread()) {
ui_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Core::ChangeResolution, this, resolution));
return;
}
if (rdp_client_window_) {
rdp_client_window_->ChangeResolution(resolution);
}
}
void RdpClient::Core::OnConnected() {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
DCHECK(rdp_client_window_);
NotifyConnected();
}
void RdpClient::Core::OnDisconnected() {
DCHECK(ui_task_runner_->BelongsToCurrentThread());
DCHECK(rdp_client_window_);
NotifyClosed();
ui_task_runner_->DeleteSoon(FROM_HERE, rdp_client_window_.release());
self_ = nullptr;
}
RdpClient::Core::~Core() {
DCHECK(!event_handler_);
DCHECK(!rdp_client_window_);
}
void RdpClient::Core::NotifyConnected() {
if (!caller_task_runner_->BelongsToCurrentThread()) {
caller_task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Core::NotifyConnected, this));
return;
}
if (event_handler_) {
event_handler_->OnRdpConnected();
}
}
void RdpClient::Core::NotifyClosed() {
if (!caller_task_runner_->BelongsToCurrentThread()) {
caller_task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Core::NotifyClosed, this));
return;
}
if (event_handler_) {
RdpClient::EventHandler* event_handler = event_handler_;
event_handler_ = nullptr;
event_handler->OnRdpClosed();
}
}
}