e099d79879
This macro checks if a pointer is valid _after_ we've already used that pointer. So it will only trigger if we're already performed some for of buffer overflow. As such, it provides little to no value and can only server to encourage broken behaviour. Let's remove it and replace it with proper bounds checking before access instead.
550 lines
10 KiB
C
550 lines
10 KiB
C
/* -*- c-basic-offset: 8 -*-
|
|
rdesktop: A Remote Desktop Protocol client.
|
|
Seamless Windows support
|
|
Copyright 2005-2008 Peter Astrand <astrand@cendio.se> for Cendio AB
|
|
Copyright 2007-2008 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
|
Copyright 2013-2017 Henrik Andersson <hean01@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"
|
|
#include <stdarg.h>
|
|
#include <assert.h>
|
|
|
|
extern RD_BOOL g_seamless_rdp;
|
|
static VCHANNEL *seamless_channel;
|
|
static unsigned int seamless_serial;
|
|
static char *seamless_rest = NULL;
|
|
static char icon_buf[1024];
|
|
|
|
static char *
|
|
seamless_get_token(char **s)
|
|
{
|
|
char *comma, *head;
|
|
head = *s;
|
|
|
|
if (!head)
|
|
return NULL;
|
|
|
|
comma = strchr(head, ',');
|
|
if (comma)
|
|
{
|
|
*comma = '\0';
|
|
*s = comma + 1;
|
|
}
|
|
else
|
|
{
|
|
*s = NULL;
|
|
}
|
|
|
|
return head;
|
|
}
|
|
|
|
|
|
static RD_BOOL
|
|
seamless_process_line(const char *line, void *data)
|
|
{
|
|
UNUSED(data);
|
|
char *p, *l;
|
|
char *tok1, *tok3, *tok4, *tok5, *tok6, *tok7, *tok8;
|
|
unsigned long id, flags;
|
|
char *endptr;
|
|
|
|
l = xstrdup(line);
|
|
p = l;
|
|
|
|
logger(Core, Debug, "seamless_process_line(), got '%s'", p);
|
|
|
|
tok1 = seamless_get_token(&p);
|
|
(void) seamless_get_token(&p);
|
|
tok3 = seamless_get_token(&p);
|
|
tok4 = seamless_get_token(&p);
|
|
tok5 = seamless_get_token(&p);
|
|
tok6 = seamless_get_token(&p);
|
|
tok7 = seamless_get_token(&p);
|
|
tok8 = seamless_get_token(&p);
|
|
|
|
if (!strcmp("CREATE", tok1))
|
|
{
|
|
unsigned long group, parent;
|
|
if (!tok6)
|
|
return False;
|
|
|
|
id = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
group = strtoul(tok4, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
parent = strtoul(tok5, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
flags = strtoul(tok6, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_create_window(id, group, parent, flags);
|
|
}
|
|
else if (!strcmp("DESTROY", tok1))
|
|
{
|
|
if (!tok4)
|
|
return False;
|
|
|
|
id = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
flags = strtoul(tok4, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_destroy_window(id, flags);
|
|
|
|
}
|
|
else if (!strcmp("DESTROYGRP", tok1))
|
|
{
|
|
if (!tok4)
|
|
return False;
|
|
|
|
id = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
flags = strtoul(tok4, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_destroy_group(id, flags);
|
|
}
|
|
else if (!strcmp("SETICON", tok1))
|
|
{
|
|
int chunk, width, height, len;
|
|
char byte[3];
|
|
|
|
if (!tok8)
|
|
return False;
|
|
|
|
id = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
chunk = strtoul(tok4, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
width = strtoul(tok6, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
height = strtoul(tok7, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
byte[2] = '\0';
|
|
len = 0;
|
|
while (*tok8 != '\0')
|
|
{
|
|
byte[0] = *tok8;
|
|
tok8++;
|
|
if (*tok8 == '\0')
|
|
return False;
|
|
byte[1] = *tok8;
|
|
tok8++;
|
|
|
|
icon_buf[len] = strtol(byte, NULL, 16);
|
|
len++;
|
|
|
|
if ((size_t)len >= sizeof(icon_buf))
|
|
{
|
|
logger(Protocol, Warning, "seamless_process_line(), icon data would overrun icon_buf");
|
|
break;
|
|
}
|
|
}
|
|
|
|
ui_seamless_seticon(id, tok5, width, height, chunk, icon_buf, len);
|
|
}
|
|
else if (!strcmp("DELICON", tok1))
|
|
{
|
|
int width, height;
|
|
|
|
if (!tok6)
|
|
return False;
|
|
|
|
id = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
width = strtoul(tok5, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
height = strtoul(tok6, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_delicon(id, tok4, width, height);
|
|
}
|
|
else if (!strcmp("POSITION", tok1))
|
|
{
|
|
int x, y, width, height;
|
|
|
|
if (!tok8)
|
|
return False;
|
|
|
|
id = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
x = strtol(tok4, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
y = strtol(tok5, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
width = strtol(tok6, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
height = strtol(tok7, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
flags = strtoul(tok8, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_move_window(id, x, y, width, height, flags);
|
|
}
|
|
else if (!strcmp("ZCHANGE", tok1))
|
|
{
|
|
unsigned long behind;
|
|
|
|
id = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
behind = strtoul(tok4, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
flags = strtoul(tok5, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_restack_window(id, behind, flags);
|
|
}
|
|
else if (!strcmp("TITLE", tok1))
|
|
{
|
|
if (!tok5)
|
|
return False;
|
|
|
|
id = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
flags = strtoul(tok5, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_settitle(id, tok4, flags);
|
|
}
|
|
else if (!strcmp("STATE", tok1))
|
|
{
|
|
unsigned int state;
|
|
|
|
if (!tok5)
|
|
return False;
|
|
|
|
id = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
state = strtoul(tok4, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
flags = strtoul(tok5, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_setstate(id, state, flags);
|
|
}
|
|
else if (!strcmp("DEBUG", tok1))
|
|
{
|
|
logger(Core, Debug, "seamless_process_line(), %s", line);
|
|
}
|
|
else if (!strcmp("SYNCBEGIN", tok1))
|
|
{
|
|
if (!tok3)
|
|
return False;
|
|
|
|
flags = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_syncbegin(flags);
|
|
}
|
|
else if (!strcmp("SYNCEND", tok1))
|
|
{
|
|
if (!tok3)
|
|
return False;
|
|
|
|
flags = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
/* do nothing, currently */
|
|
}
|
|
else if (!strcmp("HELLO", tok1))
|
|
{
|
|
if (!tok3)
|
|
return False;
|
|
|
|
flags = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_begin(! !(flags & SEAMLESSRDP_HELLO_HIDDEN));
|
|
}
|
|
else if (!strcmp("ACK", tok1))
|
|
{
|
|
unsigned int serial;
|
|
|
|
serial = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_ack(serial);
|
|
}
|
|
else if (!strcmp("HIDE", tok1))
|
|
{
|
|
if (!tok3)
|
|
return False;
|
|
|
|
flags = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_hide_desktop();
|
|
}
|
|
else if (!strcmp("UNHIDE", tok1))
|
|
{
|
|
if (!tok3)
|
|
return False;
|
|
|
|
flags = strtoul(tok3, &endptr, 0);
|
|
if (*endptr)
|
|
return False;
|
|
|
|
ui_seamless_unhide_desktop();
|
|
}
|
|
|
|
|
|
xfree(l);
|
|
return True;
|
|
}
|
|
|
|
|
|
static RD_BOOL
|
|
seamless_line_handler(const char *line, void *data)
|
|
{
|
|
if (!seamless_process_line(line, data))
|
|
{
|
|
logger(Core, Warning, "seamless_line_handler(), invalid request '%s'", line);
|
|
}
|
|
return True;
|
|
}
|
|
|
|
|
|
static void
|
|
seamless_process(STREAM s)
|
|
{
|
|
unsigned int pkglen;
|
|
char *buf;
|
|
|
|
pkglen = s->end - s->p;
|
|
/* str_handle_lines requires null terminated strings */
|
|
buf = xmalloc(pkglen + 1);
|
|
STRNCPY(buf, (char *) s->p, pkglen + 1);
|
|
str_handle_lines(buf, &seamless_rest, seamless_line_handler, NULL);
|
|
|
|
xfree(buf);
|
|
}
|
|
|
|
|
|
RD_BOOL
|
|
seamless_init(void)
|
|
{
|
|
if (!g_seamless_rdp)
|
|
return False;
|
|
|
|
seamless_serial = 0;
|
|
|
|
seamless_channel =
|
|
channel_register("seamrdp", CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP,
|
|
seamless_process);
|
|
return (seamless_channel != NULL);
|
|
}
|
|
|
|
void
|
|
seamless_reset_state(void)
|
|
{
|
|
if (seamless_rest != NULL)
|
|
{
|
|
xfree(seamless_rest);
|
|
seamless_rest = NULL;
|
|
}
|
|
}
|
|
|
|
static unsigned int
|
|
seamless_send(const char *command, const char *format, ...)
|
|
{
|
|
STREAM s;
|
|
size_t len;
|
|
va_list argp;
|
|
char *escaped, buf[1025];
|
|
|
|
len = snprintf(buf, sizeof(buf) - 1, "%s,%u,", command, seamless_serial);
|
|
|
|
assert(len < (sizeof(buf) - 1));
|
|
|
|
va_start(argp, format);
|
|
len += vsnprintf(buf + len, sizeof(buf) - len - 1, format, argp);
|
|
va_end(argp);
|
|
|
|
assert(len < (sizeof(buf) - 1));
|
|
|
|
escaped = utils_string_escape(buf);
|
|
len = snprintf(buf, sizeof(buf), "%s", escaped);
|
|
free(escaped);
|
|
assert(len < (sizeof(buf) - 1));
|
|
|
|
buf[len] = '\n';
|
|
buf[len + 1] = '\0';
|
|
|
|
len++;
|
|
|
|
s = channel_init(seamless_channel, len);
|
|
out_uint8p(s, buf, len) s_mark_end(s);
|
|
|
|
logger(Core, Debug, "seamless_send(), sending '%s'", buf);
|
|
|
|
channel_send(s, seamless_channel);
|
|
|
|
return seamless_serial++;
|
|
}
|
|
|
|
|
|
unsigned int
|
|
seamless_send_sync()
|
|
{
|
|
if (!g_seamless_rdp)
|
|
return (unsigned int) -1;
|
|
|
|
return seamless_send("SYNC", "");
|
|
}
|
|
|
|
|
|
unsigned int
|
|
seamless_send_state(unsigned long id, unsigned int state, unsigned long flags)
|
|
{
|
|
if (!g_seamless_rdp)
|
|
return (unsigned int) -1;
|
|
|
|
return seamless_send("STATE", "0x%08lx,0x%x,0x%lx", id, state, flags);
|
|
}
|
|
|
|
|
|
unsigned int
|
|
seamless_send_position(unsigned long id, int x, int y, int width, int height, unsigned long flags)
|
|
{
|
|
return seamless_send("POSITION", "0x%08lx,%d,%d,%d,%d,0x%lx", id, x, y, width, height,
|
|
flags);
|
|
}
|
|
|
|
|
|
/* Update select timeout */
|
|
void
|
|
seamless_select_timeout(struct timeval *tv)
|
|
{
|
|
struct timeval ourtimeout = { 0, SEAMLESSRDP_POSITION_TIMER };
|
|
|
|
if (g_seamless_rdp)
|
|
{
|
|
if (timercmp(&ourtimeout, tv, <))
|
|
{
|
|
tv->tv_sec = ourtimeout.tv_sec;
|
|
tv->tv_usec = ourtimeout.tv_usec;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
unsigned int
|
|
seamless_send_zchange(unsigned long id, unsigned long below, unsigned long flags)
|
|
{
|
|
if (!g_seamless_rdp)
|
|
return (unsigned int) -1;
|
|
|
|
return seamless_send("ZCHANGE", "0x%08lx,0x%08lx,0x%lx", id, below, flags);
|
|
}
|
|
|
|
|
|
|
|
unsigned int
|
|
seamless_send_focus(unsigned long id, unsigned long flags)
|
|
{
|
|
if (!g_seamless_rdp)
|
|
return (unsigned int) -1;
|
|
|
|
return seamless_send("FOCUS", "0x%08lx,0x%lx", id, flags);
|
|
}
|
|
|
|
/* Send client-to-server message to destroy process on the server. */
|
|
unsigned int
|
|
seamless_send_destroy(unsigned long id)
|
|
{
|
|
return seamless_send("DESTROY", "0x%08lx", id);
|
|
}
|
|
|
|
unsigned int
|
|
seamless_send_spawn(char *cmdline)
|
|
{
|
|
unsigned int res;
|
|
if (!g_seamless_rdp)
|
|
return (unsigned int) -1;
|
|
|
|
res = seamless_send("SPAWN", cmdline);
|
|
|
|
return res;
|
|
}
|
|
|
|
unsigned int
|
|
seamless_send_persistent(RD_BOOL enable)
|
|
{
|
|
unsigned int res;
|
|
if (!g_seamless_rdp)
|
|
return (unsigned int) -1;
|
|
|
|
logger(Core, Debug, "seamless_send_persistent(), %s persistent seamless mode",
|
|
enable ? "enable" : "disable");
|
|
res = seamless_send("PERSISTENT", "%d", enable);
|
|
|
|
return res;
|
|
}
|