Add Dynamic Virtual Channels and basic RDPEDISP support
Fix issue #192 Signed-off-by: Henrik Andersson <hean01@cendio.com> Signed-off-by: Karl Mikaelsson <derfian@cendio.se>
This commit is contained in:
parent
d3d6613c78
commit
2f03f65efe
@ -25,7 +25,7 @@ SOUNDOBJ = @SOUNDOBJ@
|
|||||||
SCARDOBJ = @SCARDOBJ@
|
SCARDOBJ = @SCARDOBJ@
|
||||||
CREDSSPOBJ = @CREDSSPOBJ@
|
CREDSSPOBJ = @CREDSSPOBJ@
|
||||||
|
|
||||||
RDPOBJ = tcp.o asn.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o ssl.o utils.o stream.o
|
RDPOBJ = tcp.o asn.o iso.o mcs.o secure.o licence.o rdp.o orders.o bitmap.o cache.o rdp5.o channels.o rdpdr.o serial.o printer.o disk.o parallel.o printercache.o mppc.o pstcache.o lspci.o seamless.o ssl.o utils.o stream.o dvc.o rdpedisp.o
|
||||||
X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o ctrl.o
|
X11OBJ = rdesktop.o xwin.o xkeymap.o ewmhints.o xclip.o cliprdr.o ctrl.o
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
|
@ -92,7 +92,7 @@ channel_send(STREAM s, VCHANNEL * channel)
|
|||||||
s_pop_layer(s, channel_hdr);
|
s_pop_layer(s, channel_hdr);
|
||||||
length = s->end - s->p - 8;
|
length = s->end - s->p - 8;
|
||||||
|
|
||||||
logger(Protocol, Debug, "channel_send(), length = %d", length);
|
logger(Protocol, Debug, "channel_send(), channel = %d, length = %d", channel->mcs_id, length);
|
||||||
|
|
||||||
thislength = MIN(length, CHANNEL_CHUNK_LENGTH);
|
thislength = MIN(length, CHANNEL_CHUNK_LENGTH);
|
||||||
/* Note: In the original clipboard implementation, this number was
|
/* Note: In the original clipboard implementation, this number was
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _CONSTANTS_H
|
||||||
|
#define _CONSTANTS_H
|
||||||
|
|
||||||
/* TCP port for Remote Desktop Protocol */
|
/* TCP port for Remote Desktop Protocol */
|
||||||
#define TCP_PORT_RDP 3389
|
#define TCP_PORT_RDP 3389
|
||||||
|
|
||||||
@ -800,3 +803,5 @@ enum RDP_SUPPRESS_STATUS
|
|||||||
SUPPRESS_DISPLAY_UPDATES = 0x00,
|
SUPPRESS_DISPLAY_UPDATES = 0x00,
|
||||||
ALLOW_DISPLAY_UPDATES = 0x01
|
ALLOW_DISPLAY_UPDATES = 0x01
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#endif /* _CONSTANTS_H */
|
||||||
|
455
dvc.c
Normal file
455
dvc.c
Normal file
@ -0,0 +1,455 @@
|
|||||||
|
/* -*- c-basic-offset: 8 -*-
|
||||||
|
rdesktop: A Remote Desktop Protocol client.
|
||||||
|
Dynamic Channel Virtual Channel Extension.
|
||||||
|
Copyright 2017 Henrik Andersson <hean01@cendio.com> for Cendio AB
|
||||||
|
Copyright 2017 Karl Mikaelsson <derfian@cendio.se> for Cendio AB
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rdesktop.h"
|
||||||
|
|
||||||
|
#define MAX_DVC_CHANNELS 20
|
||||||
|
#define INVALID_CHANNEL ((uint32)-1)
|
||||||
|
|
||||||
|
#define DYNVC_CREATE_REQ 0x01
|
||||||
|
#define DYNVC_DATA_FIRST 0x02
|
||||||
|
#define DYNVC_DATA 0x03
|
||||||
|
#define DYNVC_CLOSE 0x04
|
||||||
|
#define DYNVC_CAPABILITIES 0x05
|
||||||
|
#define DYNVC_DATA_FIRST_COMPRESSED 0x06
|
||||||
|
#define DYNVC_DATA_COMPRESSED 0x07
|
||||||
|
#define DYNVC_SOFT_SYNC_REQUEST 0x08
|
||||||
|
#define DYNVC_SOFT_SYNC_RESPONSE 0x09
|
||||||
|
|
||||||
|
static VCHANNEL *dvc_channel;
|
||||||
|
|
||||||
|
static uint32 dvc_in_channelid(STREAM s, dvc_hdr_t hdr);
|
||||||
|
|
||||||
|
typedef struct dvc_channel_t
|
||||||
|
{
|
||||||
|
uint32 hash;
|
||||||
|
uint32 channel_id;
|
||||||
|
dvc_channel_process_fn handler;
|
||||||
|
} dvc_channel_t;
|
||||||
|
|
||||||
|
dvc_channel_t channels[MAX_DVC_CHANNELS];
|
||||||
|
|
||||||
|
static RD_BOOL
|
||||||
|
dvc_channels_exists(const char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32 hash;
|
||||||
|
hash = utils_djb2_hash(name);
|
||||||
|
for (i = 0; i < MAX_DVC_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (channels[i].hash == hash)
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const dvc_channel_t *
|
||||||
|
dvc_channels_get_by_id(uint32 id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DVC_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (channels[i].channel_id == id)
|
||||||
|
{
|
||||||
|
return &channels[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32
|
||||||
|
dvc_channels_get_id(const char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32 hash;
|
||||||
|
hash = utils_djb2_hash(name);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DVC_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (channels[i].hash == hash)
|
||||||
|
{
|
||||||
|
return channels[i].channel_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return INVALID_CHANNEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RD_BOOL
|
||||||
|
dvc_channels_remove_by_id(uint32 channelid)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DVC_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (channels[i].channel_id == channelid)
|
||||||
|
{
|
||||||
|
memset(&channels[i], 0, sizeof(dvc_channel_t));
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RD_BOOL
|
||||||
|
dvc_channels_add(const char *name, dvc_channel_process_fn handler, uint32 channel_id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32 hash;
|
||||||
|
|
||||||
|
if (dvc_channels_exists(name) == True)
|
||||||
|
{
|
||||||
|
logger(Core, Warning, "dvc_channels_add(), channel with name '%s' already exists",
|
||||||
|
name);
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DVC_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (channels[i].hash == 0)
|
||||||
|
{
|
||||||
|
hash = utils_djb2_hash(name);
|
||||||
|
channels[i].hash = hash;
|
||||||
|
channels[i].handler = handler;
|
||||||
|
channels[i].channel_id = channel_id;
|
||||||
|
logger(Core, Debug,
|
||||||
|
"dvc_channels_add(), Added hash=%x, channel_id=%d, name=%s, handler=%p",
|
||||||
|
hash, channel_id, name, handler);
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger(Core, Warning, "dvc_channels_add(), Failed to add channel, maximum number of channels are being used");
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dvc_channels_set_id(const char *name, uint32 channel_id)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32 hash;
|
||||||
|
|
||||||
|
hash = utils_djb2_hash(name);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DVC_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (channels[i].hash == hash)
|
||||||
|
{
|
||||||
|
logger(Core, Debug, "dvc_channels_set_id(), name = '%s', channel_id = %d",
|
||||||
|
name, channel_id);
|
||||||
|
channels[i].channel_id = channel_id;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RD_BOOL
|
||||||
|
dvc_channels_is_available(const char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32 hash;
|
||||||
|
hash = utils_djb2_hash(name);
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DVC_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
if (channels[i].hash == hash)
|
||||||
|
{
|
||||||
|
return (channels[i].channel_id != INVALID_CHANNEL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
RD_BOOL
|
||||||
|
dvc_channels_register(const char *name, dvc_channel_process_fn handler)
|
||||||
|
{
|
||||||
|
return dvc_channels_add(name, handler, INVALID_CHANNEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static STREAM
|
||||||
|
dvc_init_packet(dvc_hdr_t hdr, uint32 channelid, size_t length)
|
||||||
|
{
|
||||||
|
STREAM s;
|
||||||
|
|
||||||
|
length += 1; /* add 1 byte hdr */
|
||||||
|
|
||||||
|
if (channelid != INVALID_CHANNEL)
|
||||||
|
{
|
||||||
|
if (hdr.hdr.cbid == 0)
|
||||||
|
length += 1;
|
||||||
|
else if (hdr.hdr.cbid == 1)
|
||||||
|
length += 2;
|
||||||
|
else if (hdr.hdr.cbid == 2)
|
||||||
|
length += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
s = channel_init(dvc_channel, length);
|
||||||
|
out_uint8(s, hdr.data); /* DVC header */
|
||||||
|
|
||||||
|
if (channelid != INVALID_CHANNEL)
|
||||||
|
{
|
||||||
|
if (hdr.hdr.cbid == 0)
|
||||||
|
{
|
||||||
|
out_uint8(s, channelid);
|
||||||
|
}
|
||||||
|
else if (hdr.hdr.cbid == 1)
|
||||||
|
{
|
||||||
|
out_uint16_le(s, channelid);
|
||||||
|
}
|
||||||
|
else if (hdr.hdr.cbid == 2)
|
||||||
|
{
|
||||||
|
out_uint32_le(s, channelid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dvc_send(const char *name, STREAM s)
|
||||||
|
{
|
||||||
|
STREAM ls;
|
||||||
|
dvc_hdr_t hdr;
|
||||||
|
uint32 channel_id;
|
||||||
|
|
||||||
|
channel_id = dvc_channels_get_id(name);
|
||||||
|
if (channel_id == INVALID_CHANNEL)
|
||||||
|
{
|
||||||
|
logger(Core, Error, "dvc_send(), Trying to send data on invalid channel '%s'",
|
||||||
|
name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: we assume length is less than 1600 */
|
||||||
|
|
||||||
|
hdr.hdr.cmd = DYNVC_DATA;
|
||||||
|
hdr.hdr.cbid = 2;
|
||||||
|
hdr.hdr.sp = 0;
|
||||||
|
|
||||||
|
ls = dvc_init_packet(hdr, channel_id, s_length(s));
|
||||||
|
|
||||||
|
out_stream(ls, s);
|
||||||
|
|
||||||
|
s_mark_end(ls);
|
||||||
|
|
||||||
|
channel_send(ls, dvc_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
dvc_send_capabilities_response()
|
||||||
|
{
|
||||||
|
STREAM s;
|
||||||
|
dvc_hdr_t hdr;
|
||||||
|
uint16 supportedversion = 0x01;
|
||||||
|
|
||||||
|
hdr.hdr.cbid = 0x00;
|
||||||
|
hdr.hdr.sp = 0x00;
|
||||||
|
hdr.hdr.cmd = DYNVC_CAPABILITIES;
|
||||||
|
|
||||||
|
logger(Protocol, Debug,
|
||||||
|
"dvc_send_capabilities_response(), offering support for dvc %d", supportedversion);
|
||||||
|
|
||||||
|
s = dvc_init_packet(hdr, -1, 3);
|
||||||
|
out_uint8(s, 0x00); /* pad */
|
||||||
|
out_uint16_le(s, supportedversion); /* version */
|
||||||
|
|
||||||
|
s_mark_end(s);
|
||||||
|
|
||||||
|
channel_send(s, dvc_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dvc_process_caps_pdu(STREAM s)
|
||||||
|
{
|
||||||
|
uint16 version;
|
||||||
|
|
||||||
|
/* VERSION1 */
|
||||||
|
in_uint8s(s, 1); /* pad */
|
||||||
|
in_uint16_le(s, version); /* version */
|
||||||
|
|
||||||
|
logger(Protocol, Debug, "dvc_process_caps(), server supports dvc %d", version);
|
||||||
|
|
||||||
|
dvc_send_capabilities_response();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dvc_send_create_response(RD_BOOL success, dvc_hdr_t hdr, uint32 channelid)
|
||||||
|
{
|
||||||
|
STREAM s;
|
||||||
|
|
||||||
|
logger(Protocol, Debug, "dvc_send_create_response(), %s request to create channelid %d",
|
||||||
|
(success ? "granted" : "denied"), channelid);
|
||||||
|
s = dvc_init_packet(hdr, channelid, 4);
|
||||||
|
out_uint32_le(s, success ? 0 : -1);
|
||||||
|
s_mark_end(s);
|
||||||
|
|
||||||
|
channel_send(s, dvc_channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dvc_process_create_pdu(STREAM s, dvc_hdr_t hdr)
|
||||||
|
{
|
||||||
|
char name[512];
|
||||||
|
uint32 channelid;
|
||||||
|
|
||||||
|
channelid = dvc_in_channelid(s, hdr);
|
||||||
|
|
||||||
|
in_ansi_string(s, name, sizeof(name));
|
||||||
|
|
||||||
|
logger(Protocol, Debug, "dvc_process_create(), server requests channelid = %d, name = '%s'",
|
||||||
|
channelid, name);
|
||||||
|
|
||||||
|
if (dvc_channels_exists(name))
|
||||||
|
{
|
||||||
|
logger(Core, Verbose, "Established dynamic virtual channel '%s'", name);
|
||||||
|
|
||||||
|
dvc_channels_set_id(name, channelid);
|
||||||
|
dvc_send_create_response(True, hdr, channelid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dvc_send_create_response(False, hdr, channelid);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32
|
||||||
|
dvc_in_channelid(STREAM s, dvc_hdr_t hdr)
|
||||||
|
{
|
||||||
|
uint32 id;
|
||||||
|
|
||||||
|
id = (uint32) - 1;
|
||||||
|
|
||||||
|
switch (hdr.hdr.cbid)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
in_uint8(s, id);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
in_uint16_le(s, id);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
in_uint32_le(s, id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dvc_process_data_pdu(STREAM s, dvc_hdr_t hdr)
|
||||||
|
{
|
||||||
|
const dvc_channel_t *ch;
|
||||||
|
uint32 channelid;
|
||||||
|
|
||||||
|
channelid = dvc_in_channelid(s, hdr);
|
||||||
|
ch = dvc_channels_get_by_id(channelid);
|
||||||
|
if (ch == NULL)
|
||||||
|
{
|
||||||
|
logger(Protocol, Warning,
|
||||||
|
"dvc_process_data(), Received data on unregistered channel %d", channelid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dispatch packet to channel handler */
|
||||||
|
ch->handler(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dvc_process_close_pdu(STREAM s, dvc_hdr_t hdr)
|
||||||
|
{
|
||||||
|
uint32 channelid;
|
||||||
|
|
||||||
|
channelid = dvc_in_channelid(s, hdr);
|
||||||
|
logger(Protocol, Debug, "dvc_process_close_pdu(), close channel %d", channelid);
|
||||||
|
|
||||||
|
if (!dvc_channels_remove_by_id(channelid))
|
||||||
|
{
|
||||||
|
logger(Protocol, Warning,
|
||||||
|
"dvc_process_close_pdu(), Received close request for unregistered channel %d",
|
||||||
|
channelid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
dvc_process_pdu(STREAM s)
|
||||||
|
{
|
||||||
|
dvc_hdr_t hdr;
|
||||||
|
|
||||||
|
in_uint8(s, hdr.data);
|
||||||
|
|
||||||
|
switch (hdr.hdr.cmd)
|
||||||
|
{
|
||||||
|
case DYNVC_CAPABILITIES:
|
||||||
|
dvc_process_caps_pdu(s);
|
||||||
|
break;
|
||||||
|
case DYNVC_CREATE_REQ:
|
||||||
|
dvc_process_create_pdu(s, hdr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DYNVC_DATA:
|
||||||
|
dvc_process_data_pdu(s, hdr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DYNVC_CLOSE:
|
||||||
|
dvc_process_close_pdu(s, hdr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#if 0 /* Unimplemented */
|
||||||
|
|
||||||
|
case DYNVC_DATA_FIRST:
|
||||||
|
break;
|
||||||
|
case DYNVC_DATA_FIRST_COMPRESSED:
|
||||||
|
break;
|
||||||
|
case DYNVC_DATA_COMPRESSED:
|
||||||
|
break;
|
||||||
|
case DYNVC_SOFT_SYNC_REQUEST:
|
||||||
|
break;
|
||||||
|
case DYNVC_SOFT_SYNC_RESPONSE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
default:
|
||||||
|
logger(Protocol, Warning, "dvc_process_pdu(), Unhandled command type 0x%x",
|
||||||
|
hdr.hdr.cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RD_BOOL
|
||||||
|
dvc_init()
|
||||||
|
{
|
||||||
|
memset(channels, 0, sizeof(channels));
|
||||||
|
dvc_channel = channel_register("drdynvc",
|
||||||
|
CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
|
||||||
|
dvc_process_pdu);
|
||||||
|
|
||||||
|
return (dvc_channel != NULL);
|
||||||
|
}
|
9
proto.h
9
proto.h
@ -327,6 +327,15 @@ void ui_seamless_syncbegin(unsigned long flags);
|
|||||||
void ui_seamless_ack(unsigned int serial);
|
void ui_seamless_ack(unsigned int serial);
|
||||||
/* lspci.c */
|
/* lspci.c */
|
||||||
RD_BOOL lspci_init(void);
|
RD_BOOL lspci_init(void);
|
||||||
|
/* rdpedisp.c */
|
||||||
|
void rdpedisp_init(void);
|
||||||
|
void rdpedisp_set_session_size(uint32 width, uint32 height);
|
||||||
|
/* dvc.c */
|
||||||
|
typedef void (*dvc_channel_process_fn) (STREAM s);
|
||||||
|
RD_BOOL dvc_init(void);
|
||||||
|
RD_BOOL dvc_channels_register(const char *name, dvc_channel_process_fn handler);
|
||||||
|
RD_BOOL dvc_channels_is_available(const char *name);
|
||||||
|
void dvc_send(const char *name, STREAM s);
|
||||||
/* seamless.c */
|
/* seamless.c */
|
||||||
RD_BOOL seamless_init(void);
|
RD_BOOL seamless_init(void);
|
||||||
void seamless_reset_state(void);
|
void seamless_reset_state(void);
|
||||||
|
@ -74,6 +74,8 @@ int g_sizeopt = 0; /* If non-zero, a special size has been
|
|||||||
int g_dpi = 0; /* device DPI: default not set */
|
int g_dpi = 0; /* device DPI: default not set */
|
||||||
int g_width = 800;
|
int g_width = 800;
|
||||||
int g_height = 600;
|
int g_height = 600;
|
||||||
|
uint32 g_windowed_width = 800;
|
||||||
|
uint32 g_windowed_height = 600;
|
||||||
int g_xpos = 0;
|
int g_xpos = 0;
|
||||||
int g_ypos = 0;
|
int g_ypos = 0;
|
||||||
int g_pos = 0; /* 0 position unspecified,
|
int g_pos = 0; /* 0 position unspecified,
|
||||||
@ -766,6 +768,9 @@ main(int argc, char *argv[])
|
|||||||
g_ypos = strtol(p, NULL, 10);
|
g_ypos = strtol(p, NULL, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_windowed_height = g_height;
|
||||||
|
g_windowed_width = g_width;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'f':
|
case 'f':
|
||||||
@ -1208,6 +1213,10 @@ main(int argc, char *argv[])
|
|||||||
lspci_init();
|
lspci_init();
|
||||||
|
|
||||||
rdpdr_init();
|
rdpdr_init();
|
||||||
|
|
||||||
|
dvc_init();
|
||||||
|
rdpedisp_init();
|
||||||
|
|
||||||
g_reconnect_loop = False;
|
g_reconnect_loop = False;
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
138
rdpedisp.c
Normal file
138
rdpedisp.c
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
/* -*- c-basic-offset: 8 -*-
|
||||||
|
rdesktop: A Remote Desktop Protocol client.
|
||||||
|
Display Update Virtual Channel Extension.
|
||||||
|
Copyright 2017 Henrik Andersson <hean01@cendio.com> for Cendio AB
|
||||||
|
Copyright 2017 Karl Mikaelsson <derfian@cendio.se> for Cendio AB
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rdesktop.h"
|
||||||
|
|
||||||
|
#define DISPLAYCONTROL_PDU_TYPE_CAPS 0x5
|
||||||
|
#define DISPLAYCONTROL_PDU_TYPE_MONITOR_LAYOUT 0x2
|
||||||
|
|
||||||
|
#define DISPLAYCONTROL_MONITOR_PRIMARY 0x1
|
||||||
|
#define RDPEDISP_CHANNEL_NAME "Microsoft::Windows::RDS::DisplayControl"
|
||||||
|
|
||||||
|
static void rdpedisp_send(STREAM s);
|
||||||
|
static void rdpedisp_init_packet(STREAM s, uint32 type, uint32 length);
|
||||||
|
|
||||||
|
static void
|
||||||
|
rdpedisp_process_caps_pdu(STREAM s)
|
||||||
|
{
|
||||||
|
uint32 tmp[3];
|
||||||
|
|
||||||
|
in_uint32_le(s, tmp[0]); /* MaxNumMonitors */
|
||||||
|
in_uint32_le(s, tmp[1]); /* MaxMonitorAreaFactorA */
|
||||||
|
in_uint32_le(s, tmp[2]); /* MaxMonitorAreaFactorB */
|
||||||
|
|
||||||
|
logger(Protocol, Debug,
|
||||||
|
"rdpedisp_process_caps_pdu(), Max supported monitor area (square pixels) is %d",
|
||||||
|
tmp[0] * tmp[1] * tmp[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rdpedisp_process_pdu(STREAM s)
|
||||||
|
{
|
||||||
|
uint32 type;
|
||||||
|
|
||||||
|
/* Read DISPLAYCONTROL_HEADER */
|
||||||
|
in_uint32_le(s, type); /* type */
|
||||||
|
in_skip(s, 4); /* length */
|
||||||
|
|
||||||
|
logger(Protocol, Debug, "rdpedisp_process_pdu(), Got PDU type %d", type);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case DISPLAYCONTROL_PDU_TYPE_CAPS:
|
||||||
|
rdpedisp_process_caps_pdu(s);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
logger(Protocol, Warning, "rdpedisp_process_pdu(), Unhandled PDU type %d",
|
||||||
|
type);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rdpedisp_send_monitor_layout_pdu(uint32 width, uint32 height)
|
||||||
|
{
|
||||||
|
struct stream s;
|
||||||
|
uint32 physwidth, physheight, desktopscale, devicescale;
|
||||||
|
|
||||||
|
memset(&s, 0, sizeof(s));
|
||||||
|
|
||||||
|
logger(Protocol, Debug, "rdpedisp_send_monitor_layout_pdu(), width = %d, height = %d", width,
|
||||||
|
height);
|
||||||
|
|
||||||
|
rdpedisp_init_packet(&s, DISPLAYCONTROL_PDU_TYPE_MONITOR_LAYOUT, 16 + 1 * 40);
|
||||||
|
|
||||||
|
out_uint32_le(&s, 40); /* MonitorLayoutSize - spec mandates 40 */
|
||||||
|
out_uint32_le(&s, 1); /* NumMonitors */
|
||||||
|
|
||||||
|
out_uint32_le(&s, DISPLAYCONTROL_MONITOR_PRIMARY); /* flags */
|
||||||
|
out_uint32_le(&s, 0); /* left */
|
||||||
|
out_uint32_le(&s, 0); /* top */
|
||||||
|
out_uint32_le(&s, width); /* width */
|
||||||
|
out_uint32_le(&s, height); /* height */
|
||||||
|
|
||||||
|
utils_calculate_dpi_scale_factors(&physwidth, &physheight, &desktopscale, &devicescale);
|
||||||
|
out_uint32_le(&s, physwidth); /* physicalwidth */
|
||||||
|
out_uint32_le(&s, physheight); /* physicalheight */
|
||||||
|
out_uint32_le(&s, ORIENTATION_LANDSCAPE); /* Orientation */
|
||||||
|
out_uint32_le(&s, desktopscale); /* DesktopScaleFactor */
|
||||||
|
out_uint32_le(&s, devicescale); /* DeviceScaleFactor */
|
||||||
|
s_mark_end(&s);
|
||||||
|
|
||||||
|
rdpedisp_send(&s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rdpedisp_init_packet(STREAM s, uint32 type, uint32 length)
|
||||||
|
{
|
||||||
|
s_realloc(s, length);
|
||||||
|
s_reset(s);
|
||||||
|
|
||||||
|
out_uint32_le(s, type);
|
||||||
|
out_uint32_le(s, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rdpedisp_send(STREAM s)
|
||||||
|
{
|
||||||
|
dvc_send(RDPEDISP_CHANNEL_NAME, s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RD_BOOL
|
||||||
|
rdpedisp_is_available()
|
||||||
|
{
|
||||||
|
return dvc_channels_is_available(RDPEDISP_CHANNEL_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rdpedisp_set_session_size(uint32 width, uint32 height)
|
||||||
|
{
|
||||||
|
if (rdpedisp_is_available() == False)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rdpedisp_send_monitor_layout_pdu(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rdpedisp_init(void)
|
||||||
|
{
|
||||||
|
dvc_channels_register(RDPEDISP_CHANNEL_NAME, rdpedisp_process_pdu);
|
||||||
|
}
|
15
secure.c
15
secure.c
@ -397,6 +397,7 @@ sec_out_mcs_connect_initial_pdu(STREAM s, uint32 selected_protocol)
|
|||||||
uint32 rdpversion = RDP_40;
|
uint32 rdpversion = RDP_40;
|
||||||
uint16 capflags = RNS_UD_CS_SUPPORT_ERRINFO_PDU;
|
uint16 capflags = RNS_UD_CS_SUPPORT_ERRINFO_PDU;
|
||||||
uint16 colorsupport = RNS_UD_24BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_32BPP_SUPPORT;
|
uint16 colorsupport = RNS_UD_24BPP_SUPPORT | RNS_UD_16BPP_SUPPORT | RNS_UD_32BPP_SUPPORT;
|
||||||
|
uint32 physwidth, physheight, desktopscale, devicescale;
|
||||||
|
|
||||||
if (g_rdp_version >= RDP_V5)
|
if (g_rdp_version >= RDP_V5)
|
||||||
rdpversion = RDP_50;
|
rdpversion = RDP_50;
|
||||||
@ -458,13 +459,13 @@ sec_out_mcs_connect_initial_pdu(STREAM s, uint32 selected_protocol)
|
|||||||
if (g_dpi > 0)
|
if (g_dpi > 0)
|
||||||
{
|
{
|
||||||
/* Extended client info describing monitor geometry */
|
/* Extended client info describing monitor geometry */
|
||||||
out_uint32_le(s, g_width * 254 / (g_dpi * 10)); /* desktop physical width */
|
utils_calculate_dpi_scale_factors(&physwidth, &physheight,
|
||||||
out_uint32_le(s, g_height * 254 / (g_dpi * 10)); /* desktop physical height */
|
&desktopscale, &devicescale);
|
||||||
out_uint16_le(s, ORIENTATION_LANDSCAPE);
|
out_uint32_le(s, physwidth); /* physicalwidth */
|
||||||
out_uint32_le(s, g_dpi < 96 ? 100 : (g_dpi * 100 + 48) / 96); /* desktop scale factor */
|
out_uint32_le(s, physheight); /* physicalheight */
|
||||||
/* the spec calls this out as being valid for range 100-500 but I doubt the upper range is accurate */
|
out_uint16_le(s, ORIENTATION_LANDSCAPE); /* Orientation */
|
||||||
out_uint32_le(s, g_dpi < 134 ? 100 : (g_dpi < 173 ? 140 : 180)); /* device scale factor */
|
out_uint32_le(s, desktopscale); /* DesktopScaleFactor */
|
||||||
/* the only allowed values for device scale factor are 100, 140, and 180. */
|
out_uint32_le(s, devicescale); /* DeviceScaleFactor */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a Client Cluster Data (TS_UD_CS_CLUSTER) */
|
/* Write a Client Cluster Data (TS_UD_CS_CLUSTER) */
|
||||||
|
27
stream.c
27
stream.c
@ -153,3 +153,30 @@ out_utf16s_no_eos(STREAM s, const char *string)
|
|||||||
{
|
{
|
||||||
_out_utf16s(s, 0, string);
|
_out_utf16s(s, 0, string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Read bytes from STREAM s into *string until a null terminator is
|
||||||
|
found, or len bytes are read from the stream. Returns the number of
|
||||||
|
bytes read. */
|
||||||
|
size_t
|
||||||
|
in_ansi_string(STREAM s, char *string, size_t len)
|
||||||
|
{
|
||||||
|
char *ps;
|
||||||
|
size_t left;
|
||||||
|
ps = string;
|
||||||
|
|
||||||
|
left = len;
|
||||||
|
while(left--)
|
||||||
|
{
|
||||||
|
if (left == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
in_uint8(s, *ps);
|
||||||
|
|
||||||
|
if (*ps == '\0')
|
||||||
|
break;
|
||||||
|
|
||||||
|
ps++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len - left;
|
||||||
|
}
|
||||||
|
4
stream.h
4
stream.h
@ -47,6 +47,9 @@ void out_utf16s(STREAM s, const char *string);
|
|||||||
void out_utf16s_padded(STREAM s, const char *string, size_t width, unsigned char pad);
|
void out_utf16s_padded(STREAM s, const char *string, size_t width, unsigned char pad);
|
||||||
void out_utf16s_no_eos(STREAM s, const char *string);
|
void out_utf16s_no_eos(STREAM s, const char *string);
|
||||||
|
|
||||||
|
size_t in_ansi_string(STREAM s, char *string, size_t len);
|
||||||
|
|
||||||
|
|
||||||
#define s_push_layer(s,h,n) { (s)->h = (s)->p; (s)->p += n; }
|
#define s_push_layer(s,h,n) { (s)->h = (s)->p; (s)->p += n; }
|
||||||
#define s_pop_layer(s,h) (s)->p = (s)->h;
|
#define s_pop_layer(s,h) (s)->p = (s)->h;
|
||||||
#define s_mark_end(s) (s)->end = (s)->p;
|
#define s_mark_end(s) (s)->end = (s)->p;
|
||||||
@ -100,6 +103,7 @@ void out_utf16s_no_eos(STREAM s, const char *string);
|
|||||||
#define in_uint8p(s,v,n) { v = (s)->p; (s)->p += n; }
|
#define in_uint8p(s,v,n) { v = (s)->p; (s)->p += n; }
|
||||||
#define in_uint8a(s,v,n) { memcpy(v,(s)->p,n); (s)->p += n; }
|
#define in_uint8a(s,v,n) { memcpy(v,(s)->p,n); (s)->p += n; }
|
||||||
#define in_uint8s(s,n) (s)->p += n;
|
#define in_uint8s(s,n) (s)->p += n;
|
||||||
|
#define in_skip(s,n) in_uint8s(s,n)
|
||||||
#define out_uint8(s,v) *((s)->p++) = v;
|
#define out_uint8(s,v) *((s)->p++) = v;
|
||||||
#define out_uint8p(s,v,n) { memcpy((s)->p,v,n); (s)->p += n; }
|
#define out_uint8p(s,v,n) { memcpy((s)->p,v,n); (s)->p += n; }
|
||||||
#define out_uint8a(s,v,n) out_uint8p(s,v,n);
|
#define out_uint8a(s,v,n) out_uint8p(s,v,n);
|
||||||
|
18
types.h
18
types.h
@ -19,6 +19,12 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _TYPES_H
|
||||||
|
#define _TYPES_H
|
||||||
|
|
||||||
|
#include "constants.h"
|
||||||
|
#include "stream.h"
|
||||||
|
|
||||||
typedef int RD_BOOL;
|
typedef int RD_BOOL;
|
||||||
|
|
||||||
#ifndef True
|
#ifndef True
|
||||||
@ -300,4 +306,16 @@ typedef struct fileinfo
|
|||||||
}
|
}
|
||||||
FILEINFO;
|
FILEINFO;
|
||||||
|
|
||||||
|
|
||||||
|
typedef union dvc_hdr_t {
|
||||||
|
uint8 data;
|
||||||
|
struct {
|
||||||
|
uint8 cbid:2;
|
||||||
|
uint8 sp:2;
|
||||||
|
uint8 cmd:4;
|
||||||
|
} hdr;
|
||||||
|
} dvc_hdr_t;
|
||||||
|
|
||||||
typedef RD_BOOL(*str_handle_lines_t) (const char *line, void *data);
|
typedef RD_BOOL(*str_handle_lines_t) (const char *line, void *data);
|
||||||
|
|
||||||
|
#endif /* _TYPES_H */
|
||||||
|
42
utils.c
42
utils.c
@ -28,8 +28,27 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
extern char g_codepage[16];
|
extern char g_codepage[16];
|
||||||
|
extern int g_dpi;
|
||||||
|
extern int g_width;
|
||||||
|
extern int g_height;
|
||||||
|
|
||||||
static RD_BOOL g_iconv_works = True;
|
static RD_BOOL g_iconv_works = True;
|
||||||
|
|
||||||
|
uint32
|
||||||
|
utils_djb2_hash(const char *str)
|
||||||
|
{
|
||||||
|
uint8 c;
|
||||||
|
uint8 *pstr;
|
||||||
|
uint32 hash = 5381;
|
||||||
|
|
||||||
|
pstr = (uint8*)str;
|
||||||
|
while ((c = *pstr++))
|
||||||
|
{
|
||||||
|
hash = ((hash << 5) + hash) + c;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
utils_string_escape(const char *str)
|
utils_string_escape(const char *str)
|
||||||
{
|
{
|
||||||
@ -220,6 +239,29 @@ utils_locale_to_utf8(const char *src, size_t is, char *dest, size_t os)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
utils_calculate_dpi_scale_factors(uint32 *physwidth, uint32 *physheight,
|
||||||
|
uint32 *desktopscale, uint32 *devicescale)
|
||||||
|
{
|
||||||
|
*physwidth = *physheight = *desktopscale = *devicescale = 0;
|
||||||
|
|
||||||
|
if (g_dpi > 0)
|
||||||
|
{
|
||||||
|
*physwidth = g_width * 254 / (g_dpi * 10);
|
||||||
|
*physheight = g_height * 254 / (g_dpi * 10);
|
||||||
|
|
||||||
|
/* the spec calls this out as being valid for range
|
||||||
|
100-500 but I doubt the upper range is accurate */
|
||||||
|
*desktopscale = g_dpi < 96 ? 100 : (g_dpi * 100 + 48) / 96;
|
||||||
|
|
||||||
|
/* the only allowed values for device scale factor are
|
||||||
|
100, 140, and 180. */
|
||||||
|
*devicescale = g_dpi < 134 ? 100 : (g_dpi < 173 ? 140 : 180);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* component logging
|
* component logging
|
||||||
*
|
*
|
||||||
|
5
utils.h
5
utils.h
@ -20,11 +20,16 @@
|
|||||||
#ifndef _utils_h
|
#ifndef _utils_h
|
||||||
#define _utils_h
|
#define _utils_h
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
unsigned int utils_djb2_hash(const char *str);
|
||||||
char *utils_string_escape(const char *str);
|
char *utils_string_escape(const char *str);
|
||||||
char *utils_string_unescape(const char *str);
|
char *utils_string_unescape(const char *str);
|
||||||
int utils_locale_to_utf8(const char *src, size_t is, char *dest, size_t os);
|
int utils_locale_to_utf8(const char *src, size_t is, char *dest, size_t os);
|
||||||
int utils_mkdir_safe(const char *path, int mask);
|
int utils_mkdir_safe(const char *path, int mask);
|
||||||
int utils_mkdir_p(const char *path, int mask);
|
int utils_mkdir_p(const char *path, int mask);
|
||||||
|
void utils_calculate_dpi_scale_factors(uint32 *physwidth, uint32 *physheight,
|
||||||
|
uint32 *desktopscale, uint32 *devicescale);
|
||||||
|
|
||||||
typedef enum log_level_t
|
typedef enum log_level_t
|
||||||
{
|
{
|
||||||
|
23
xwin.c
23
xwin.c
@ -45,6 +45,8 @@
|
|||||||
extern int g_sizeopt;
|
extern int g_sizeopt;
|
||||||
extern int g_width;
|
extern int g_width;
|
||||||
extern int g_height;
|
extern int g_height;
|
||||||
|
extern uint32 g_windowed_width;
|
||||||
|
extern uint32 g_windowed_height;
|
||||||
extern int g_xpos;
|
extern int g_xpos;
|
||||||
extern int g_ypos;
|
extern int g_ypos;
|
||||||
extern int g_pos;
|
extern int g_pos;
|
||||||
@ -2058,12 +2060,21 @@ ui_create_window(void)
|
|||||||
XSetWindowAttributes attribs;
|
XSetWindowAttributes attribs;
|
||||||
XClassHint *classhints;
|
XClassHint *classhints;
|
||||||
XSizeHints *sizehints;
|
XSizeHints *sizehints;
|
||||||
int wndwidth, wndheight;
|
|
||||||
long input_mask, ic_input_mask;
|
long input_mask, ic_input_mask;
|
||||||
XEvent xevent;
|
XEvent xevent;
|
||||||
|
|
||||||
wndwidth = g_fullscreen ? WidthOfScreen(g_screen) : g_width;
|
if (g_fullscreen)
|
||||||
wndheight = g_fullscreen ? HeightOfScreen(g_screen) : g_height;
|
{
|
||||||
|
g_width = WidthOfScreen(g_screen);
|
||||||
|
g_height = HeightOfScreen(g_screen);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_width = g_windowed_width;
|
||||||
|
g_height = g_windowed_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger(GUI, Debug, "ui_create_window() width = %d, height = %d", g_width, g_height);
|
||||||
|
|
||||||
/* Handle -x-y portion of geometry string */
|
/* Handle -x-y portion of geometry string */
|
||||||
if (g_xpos < 0 || (g_xpos == 0 && (g_pos & 2)))
|
if (g_xpos < 0 || (g_xpos == 0 && (g_pos & 2)))
|
||||||
@ -2073,8 +2084,8 @@ ui_create_window(void)
|
|||||||
|
|
||||||
get_window_attribs(&attribs);
|
get_window_attribs(&attribs);
|
||||||
|
|
||||||
g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, wndwidth,
|
g_wnd = XCreateWindow(g_display, RootWindowOfScreen(g_screen), g_xpos, g_ypos, g_width,
|
||||||
wndheight, 0, g_depth, InputOutput, g_visual,
|
g_height, 0, g_depth, InputOutput, g_visual,
|
||||||
CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
|
CWBackPixel | CWBackingStore | CWOverrideRedirect | CWColormap |
|
||||||
CWBorderPixel, &attribs);
|
CWBorderPixel, &attribs);
|
||||||
ewmh_set_wm_pid(g_wnd, getpid());
|
ewmh_set_wm_pid(g_wnd, getpid());
|
||||||
@ -2180,6 +2191,8 @@ ui_create_window(void)
|
|||||||
seamless_restack_test();
|
seamless_restack_test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rdpedisp_set_session_size(g_width, g_height);
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user