Blame server/proxy/modules/capture/cap_main.c

Packit Service fa4841
/**
Packit Service fa4841
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit Service fa4841
 * FreeRDP Proxy Server Session Capture Module
Packit Service fa4841
 *
Packit Service fa4841
 * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
Packit Service fa4841
 *
Packit Service fa4841
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit Service fa4841
 * you may not use this file except in compliance with the License.
Packit Service fa4841
 * You may obtain a copy of the License at
Packit Service fa4841
 *
Packit Service fa4841
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit Service fa4841
 *
Packit Service fa4841
 * Unless required by applicable law or agreed to in writing, software
Packit Service fa4841
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit Service fa4841
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit Service fa4841
 * See the License for the specific language governing permissions and
Packit Service fa4841
 * limitations under the License.
Packit Service fa4841
 */
Packit Service fa4841
Packit Service fa4841
#define TAG MODULE_TAG("capture")
Packit Service fa4841
Packit Service fa4841
#define PLUGIN_NAME "capture"
Packit Service fa4841
#define PLUGIN_DESC "stream egfx connections over tcp"
Packit Service fa4841
Packit Service fa4841
#include <errno.h>
Packit Service fa4841
#include <winpr/image.h>
Packit Service fa4841
#include <freerdp/gdi/gdi.h>
Packit Service fa4841
#include <winpr/winsock.h>
Packit Service fa4841
Packit Service fa4841
#include "pf_log.h"
Packit Service fa4841
#include "modules_api.h"
Packit Service fa4841
#include "pf_context.h"
Packit Service fa4841
#include "cap_config.h"
Packit Service fa4841
#include "cap_protocol.h"
Packit Service fa4841
Packit Service fa4841
#define BUFSIZE 8092
Packit Service fa4841
Packit Service fa4841
static proxyPluginsManager* g_plugins_manager = NULL;
Packit Service fa4841
static captureConfig config = { 0 };
Packit Service fa4841
Packit Service fa4841
static SOCKET capture_plugin_init_socket(void)
Packit Service fa4841
{
Packit Service fa4841
	int status;
Packit Service fa4841
	int sockfd;
Packit Service fa4841
	struct sockaddr_in addr = { 0 };
Packit Service fa4841
	sockfd = _socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Packit Service fa4841
Packit Service fa4841
	if (sockfd == -1)
Packit Service fa4841
		return -1;
Packit Service fa4841
Packit Service fa4841
	addr.sin_family = AF_INET;
Packit Service fa4841
	addr.sin_port = htons(config.port);
Packit Service fa4841
	inet_pton(AF_INET, config.host, &(addr.sin_addr));
Packit Service fa4841
Packit Service fa4841
	status = _connect(sockfd, (const struct sockaddr*)&addr, sizeof(addr));
Packit Service fa4841
	if (status < 0)
Packit Service fa4841
	{
Packit Service fa4841
		close(sockfd);
Packit Service fa4841
		return -1;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return sockfd;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL capture_plugin_send_data(SOCKET sockfd, const BYTE* buffer, size_t len)
Packit Service fa4841
{
Packit Service fa4841
	size_t chunk_len;
Packit Service fa4841
	int nsent;
Packit Service fa4841
Packit Service fa4841
	if (!buffer)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	while (len > 0)
Packit Service fa4841
	{
Packit Service fa4841
		chunk_len = len > BUFSIZE ? BUFSIZE : len;
Packit Service fa4841
		nsent = _send(sockfd, (const char*)buffer, chunk_len, 0);
Packit Service fa4841
		if (nsent == -1)
Packit Service fa4841
			return FALSE;
Packit Service fa4841
Packit Service fa4841
		buffer += nsent;
Packit Service fa4841
		len -= nsent;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL capture_plugin_send_packet(SOCKET sockfd, wStream* packet)
Packit Service fa4841
{
Packit Service fa4841
	size_t len;
Packit Service fa4841
	BYTE* buffer;
Packit Service fa4841
	BOOL result = FALSE;
Packit Service fa4841
Packit Service fa4841
	if (!packet)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	buffer = Stream_Buffer(packet);
Packit Service fa4841
	len = Stream_Capacity(packet);
Packit Service fa4841
Packit Service fa4841
	if (!capture_plugin_send_data(sockfd, buffer, len))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "error while transmitting frame: errno=%d", errno);
Packit Service fa4841
		goto error;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	result = TRUE;
Packit Service fa4841
Packit Service fa4841
error:
Packit Service fa4841
	Stream_Free(packet, TRUE);
Packit Service fa4841
	return result;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static SOCKET capture_plugin_get_socket(proxyData* pdata)
Packit Service fa4841
{
Packit Service fa4841
	void* custom;
Packit Service fa4841
Packit Service fa4841
	custom = g_plugins_manager->GetPluginData(PLUGIN_NAME, pdata);
Packit Service fa4841
	if (!custom)
Packit Service fa4841
		return -1;
Packit Service fa4841
Packit Service fa4841
	return (SOCKET)custom;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL capture_plugin_session_end(proxyData* pdata)
Packit Service fa4841
{
Packit Service fa4841
	SOCKET socket;
Packit Service fa4841
	BOOL ret;
Packit Service fa4841
	wStream* s;
Packit Service fa4841
Packit Service fa4841
	socket = capture_plugin_get_socket(pdata);
Packit Service fa4841
	if (socket == -1)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	s = capture_plugin_packet_new(SESSION_END_PDU_BASE_SIZE, MESSAGE_TYPE_SESSION_END);
Packit Service fa4841
	if (!s)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	ret = capture_plugin_send_packet(socket, s);
Packit Service fa4841
Packit Service fa4841
	closesocket(socket);
Packit Service fa4841
	return ret;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL capture_plugin_send_frame(pClientContext* pc, SOCKET socket, const BYTE* buffer)
Packit Service fa4841
{
Packit Service fa4841
	size_t frame_size;
Packit Service fa4841
	BOOL ret = FALSE;
Packit Service fa4841
	wStream* s = NULL;
Packit Service fa4841
	BYTE* bmp_header = NULL;
Packit Service fa4841
	rdpSettings* settings = pc->context.settings;
Packit Service fa4841
Packit Service fa4841
	frame_size = settings->DesktopWidth * settings->DesktopHeight * (settings->ColorDepth / 8);
Packit Service fa4841
	bmp_header = winpr_bitmap_construct_header(settings->DesktopWidth, settings->DesktopHeight,
Packit Service fa4841
	                                           settings->ColorDepth);
Packit Service fa4841
Packit Service fa4841
	if (!bmp_header)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	/*
Packit Service fa4841
	 * capture frame packet indicates a packet that contains a frame buffer. payload length is
Packit Service fa4841
	 * marked as 0, and receiving side must read `frame_size` bytes, a constant size of
Packit Service fa4841
	 * width*height*(bpp/8) from the socket, to receive the full frame buffer.
Packit Service fa4841
	 */
Packit Service fa4841
	s = capture_plugin_packet_new(0, MESSAGE_TYPE_CAPTURED_FRAME);
Packit Service fa4841
	if (!s)
Packit Service fa4841
		goto error;
Packit Service fa4841
Packit Service fa4841
	if (!capture_plugin_send_packet(socket, s))
Packit Service fa4841
		goto error;
Packit Service fa4841
Packit Service fa4841
	ret = capture_plugin_send_data(socket, bmp_header, WINPR_IMAGE_BMP_HEADER_LEN);
Packit Service fa4841
	if (!ret)
Packit Service fa4841
		goto error;
Packit Service fa4841
Packit Service fa4841
	ret = capture_plugin_send_data(socket, buffer, frame_size);
Packit Service fa4841
Packit Service fa4841
error:
Packit Service fa4841
	free(bmp_header);
Packit Service fa4841
	return ret;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL capture_plugin_client_end_paint(proxyData* pdata)
Packit Service fa4841
{
Packit Service fa4841
	pClientContext* pc = pdata->pc;
Packit Service fa4841
	rdpGdi* gdi = pc->context.gdi;
Packit Service fa4841
	SOCKET socket;
Packit Service fa4841
Packit Service fa4841
	if (gdi->suppressOutput)
Packit Service fa4841
		return TRUE;
Packit Service fa4841
Packit Service fa4841
	if (gdi->primary->hdc->hwnd->ninvalid < 1)
Packit Service fa4841
		return TRUE;
Packit Service fa4841
Packit Service fa4841
	socket = capture_plugin_get_socket(pdata);
Packit Service fa4841
	if (socket == -1)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	if (!capture_plugin_send_frame(pc, socket, gdi->primary_buffer))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "capture_plugin_send_frame failed!");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	gdi->primary->hdc->hwnd->invalid->null = TRUE;
Packit Service fa4841
	gdi->primary->hdc->hwnd->ninvalid = 0;
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL capture_plugin_client_post_connect(proxyData* pdata)
Packit Service fa4841
{
Packit Service fa4841
	SOCKET socket;
Packit Service fa4841
	wStream* s;
Packit Service fa4841
	pClientContext* pc = pdata->pc;
Packit Service fa4841
	rdpSettings* settings = pc->context.settings;
Packit Service fa4841
Packit Service fa4841
	socket = capture_plugin_init_socket();
Packit Service fa4841
	if (socket == -1)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "failed to establish a connection");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	g_plugins_manager->SetPluginData(PLUGIN_NAME, pdata, (void*)socket);
Packit Service fa4841
Packit Service fa4841
	s = capture_plugin_create_session_info_packet(settings);
Packit Service fa4841
	if (!s)
Packit Service fa4841
		return FALSE;
Packit Service fa4841
Packit Service fa4841
	return capture_plugin_send_packet(socket, s);
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL capture_plugin_server_post_connect(proxyData* pdata)
Packit Service fa4841
{
Packit Service fa4841
	pServerContext* ps = pdata->ps;
Packit Service fa4841
	proxyConfig* config = pdata->config;
Packit Service fa4841
	rdpSettings* settings = ps->context.settings;
Packit Service fa4841
Packit Service fa4841
	if (!config->GFX || !config->SessionCapture)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "config options 'GFX' and 'SessionCapture' options must be set to true!");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	if (!settings->SupportGraphicsPipeline)
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "session capture is only supported for GFX clients, denying connection");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static BOOL capture_plugin_unload(void)
Packit Service fa4841
{
Packit Service fa4841
	capture_plugin_config_free_internal(&config);
Packit Service fa4841
	return TRUE;
Packit Service fa4841
}
Packit Service fa4841
Packit Service fa4841
static proxyPlugin demo_plugin = {
Packit Service fa4841
	PLUGIN_NAME,                        /* name */
Packit Service fa4841
	PLUGIN_DESC,                        /* description */
Packit Service fa4841
	capture_plugin_unload,              /* PluginUnload */
Packit Service fa4841
	NULL,                               /* ClientPreConnect */
Packit Service fa4841
	capture_plugin_client_post_connect, /* ClientPostConnect */
Packit Service fa4841
	NULL,                               /* ClientLoginFailure */
Packit Service fa4841
	capture_plugin_client_end_paint,    /* ClientEndPaint */
Packit Service fa4841
	capture_plugin_server_post_connect, /* ServerPostConnect */
Packit Service fa4841
	NULL,                               /* ServerChannelsInit */
Packit Service fa4841
	NULL,                               /* ServerChannelsFree */
Packit Service fa4841
	capture_plugin_session_end,         /* Session End */
Packit Service fa4841
	NULL,                               /* KeyboardEvent */
Packit Service fa4841
	NULL,                               /* MouseEvent */
Packit Service fa4841
	NULL,                               /* ClientChannelData */
Packit Service fa4841
	NULL,                               /* ServerChannelData */
Packit Service fa4841
};
Packit Service fa4841
Packit Service fa4841
BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager)
Packit Service fa4841
{
Packit Service fa4841
	g_plugins_manager = plugins_manager;
Packit Service fa4841
Packit Service fa4841
	if (!capture_plugin_init_config(&config))
Packit Service fa4841
	{
Packit Service fa4841
		WLog_ERR(TAG, "failed to load config");
Packit Service fa4841
		return FALSE;
Packit Service fa4841
	}
Packit Service fa4841
Packit Service fa4841
	WLog_INFO(TAG, "host: %s, port: %" PRIu16 "", config.host, config.port);
Packit Service fa4841
	return plugins_manager->RegisterPlugin(&demo_plugin);
Packit Service fa4841
}