📄 rdp.c
字号:
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Protocol services - RDP layer
Copyright (C) Matthew Chapman 1999-2005
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 2 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <time.h>
#include <errno.h>
//#include <unistd.h>
#include "rdesktop.h"
#ifdef HAVE_ICONV
#ifdef HAVE_ICONV_H
#include <iconv.h>
#endif
#ifndef ICONV_CONST
#define ICONV_CONST ""
#endif
#endif
/* Receive an RDP packet */
static STREAM
rdp_recv(RDPCLIENT * This, uint8 * type)
{
static STREAM rdp_s; // FIXME HORROR
uint16 length, pdu_type;
uint8 rdpver;
if ((rdp_s == NULL) || (This->next_packet >= rdp_s->end) || (This->next_packet == NULL))
{
rdp_s = sec_recv(This, &rdpver);
if (rdp_s == NULL)
return NULL;
if (rdpver == 0xff)
{
This->next_packet = rdp_s->end;
*type = 0;
return rdp_s;
}
else if (rdpver != 3)
{
/* rdp5_process should move This->next_packet ok */
if(!rdp5_process(This, rdp_s))
return NULL;
*type = 0;
return rdp_s;
}
This->next_packet = rdp_s->p;
}
else
{
rdp_s->p = This->next_packet;
}
in_uint16_le(rdp_s, length);
/* 32k packets are really 8, keepalive fix */
if (length == 0x8000)
{
This->next_packet += 8;
*type = 0;
return rdp_s;
}
in_uint16_le(rdp_s, pdu_type);
in_uint8s(rdp_s, 2); /* userid */
*type = pdu_type & 0xf;
#if WITH_DEBUG
DEBUG(("RDP packet #%d, (type %x)\n", ++This->rdp.packetno, *type));
hexdump(This->next_packet, length);
#endif /* */
This->next_packet += length;
return rdp_s;
}
/* Initialise an RDP data packet */
static STREAM
rdp_init_data(RDPCLIENT * This, int maxlen)
{
STREAM s;
s = sec_init(This, This->encryption ? SEC_ENCRYPT : 0, maxlen + 18);
if(s == NULL)
return NULL;
s_push_layer(s, rdp_hdr, 18);
return s;
}
/* Send an RDP data packet */
static BOOL
rdp_send_data(RDPCLIENT * This, STREAM s, uint8 data_pdu_type)
{
uint16 length;
s_pop_layer(s, rdp_hdr);
length = (uint16)(s->end - s->p);
out_uint16_le(s, length);
out_uint16_le(s, (RDP_PDU_DATA | 0x10));
out_uint16_le(s, (This->mcs_userid + 1001));
out_uint32_le(s, This->rdp_shareid);
out_uint8(s, 0); /* pad */
out_uint8(s, 1); /* streamid */
out_uint16_le(s, (length - 14));
out_uint8(s, data_pdu_type);
out_uint8(s, 0); /* compress_type */
out_uint16(s, 0); /* compress_len */
return sec_send(This, s, This->encryption ? SEC_ENCRYPT : 0);
}
/* Output a string in Unicode */
void
rdp_out_unistr(RDPCLIENT * This, STREAM s, wchar_t *string, int len)
{
#ifdef HAVE_ICONV
size_t ibl = strlen(string), obl = len + 2;
static iconv_t iconv_h = (iconv_t) - 1;
char *pin = string, *pout = (char *) s->p;
memset(pout, 0, len + 4);
if (This->rdp.iconv_works)
{
if (iconv_h == (iconv_t) - 1)
{
size_t i = 1, o = 4;
if ((iconv_h = iconv_open(WINDOWS_CODEPAGE, This->codepage)) == (iconv_t) - 1)
{
warning("rdp_out_unistr: iconv_open[%s -> %s] fail %d\n",
This->codepage, WINDOWS_CODEPAGE, (int) iconv_h);
This->rdp.iconv_works = False;
rdp_out_unistr(This, s, string, len);
return;
}
if (iconv(iconv_h, (ICONV_CONST char **) &pin, &i, &pout, &o) ==
(size_t) - 1)
{
iconv_close(iconv_h);
iconv_h = (iconv_t) - 1;
warning("rdp_out_unistr: iconv(1) fail, errno %d\n", errno);
This->rdp.iconv_works = False;
rdp_out_unistr(This, s, string, len);
return;
}
pin = string;
pout = (char *) s->p;
}
if (iconv(iconv_h, (ICONV_CONST char **) &pin, &ibl, &pout, &obl) == (size_t) - 1)
{
iconv_close(iconv_h);
iconv_h = (iconv_t) - 1;
warning("rdp_out_unistr: iconv(2) fail, errno %d\n", errno);
This->rdp.iconv_works = False;
rdp_out_unistr(This, s, string, len);
return;
}
s->p += len + 2;
}
else
#endif
// TODO
{
int i = 0, j = 0;
len += 2;
while (i < len)
{
int c = string[j++];
s->p[i++] = (c >> 0) & 0xFF;
s->p[i++] = (c >> 8) & 0xFF;
}
s->p += len;
}
}
/* Input a string in Unicode
*
* Returns str_len of string
*/
int
rdp_in_unistr(RDPCLIENT * This, STREAM s, wchar_t *string, int uni_len)
{
#ifdef HAVE_ICONV
size_t ibl = uni_len, obl = uni_len;
char *pin = (char *) s->p, *pout = string;
static iconv_t iconv_h = (iconv_t) - 1;
if (This->rdp.iconv_works)
{
if (iconv_h == (iconv_t) - 1)
{
if ((iconv_h = iconv_open(This->codepage, WINDOWS_CODEPAGE)) == (iconv_t) - 1)
{
warning("rdp_in_unistr: iconv_open[%s -> %s] fail %d\n",
WINDOWS_CODEPAGE, This->codepage, (int) iconv_h);
This->rdp.iconv_works = False;
return rdp_in_unistr(This, s, string, uni_len);
}
}
if (iconv(iconv_h, (ICONV_CONST char **) &pin, &ibl, &pout, &obl) == (size_t) - 1)
{
iconv_close(iconv_h);
iconv_h = (iconv_t) - 1;
warning("rdp_in_unistr: iconv fail, errno %d\n", errno);
This->rdp.iconv_works = False;
return rdp_in_unistr(This, s, string, uni_len);
}
/* we must update the location of the current STREAM for future reads of s->p */
s->p += uni_len;
return pout - string;
}
else
#endif
// TODO
{
int i = 0;
while (i < uni_len / 2)
{
in_uint8a(s, &string[i++], 1);
in_uint8s(s, 1);
}
return i - 1;
}
}
/* Parse a logon info packet */
static BOOL
rdp_send_logon_info(RDPCLIENT * This, uint32 flags, wchar_t *domain, wchar_t *user,
wchar_t *password, wchar_t *program, wchar_t *directory)
{
wchar_t *ipaddr = tcp_get_address(This);
int len_domain = 2 * (int)wcslen(domain);
int len_user = 2 * (int)wcslen(user);
int len_password = 2 * (int)wcslen(password);
int len_program = 2 * (int)wcslen(program);
int len_directory = 2 * (int)wcslen(directory);
int len_ip = 2 * (int)wcslen(ipaddr);
int len_dll = 2 * (int)wcslen(L"C:\\WINNT\\System32\\mstscax.dll");
int packetlen = 0;
uint32 sec_flags = This->encryption ? (SEC_LOGON_INFO | SEC_ENCRYPT) : SEC_LOGON_INFO;
STREAM s;
time_t t = time(NULL);
time_t tzone;
if (!This->use_rdp5 || 1 == This->server_rdp_version)
{
DEBUG_RDP5(("Sending RDP4-style Logon packet\n"));
s = sec_init(This, sec_flags, 18 + len_domain + len_user + len_password
+ len_program + len_directory + 10);
if(s == NULL)
return False;
out_uint32(s, 0);
out_uint32_le(s, flags);
out_uint16_le(s, len_domain);
out_uint16_le(s, len_user);
out_uint16_le(s, len_password);
out_uint16_le(s, len_program);
out_uint16_le(s, len_directory);
rdp_out_unistr(This, s, domain, len_domain);
rdp_out_unistr(This, s, user, len_user);
rdp_out_unistr(This, s, password, len_password);
rdp_out_unistr(This, s, program, len_program);
rdp_out_unistr(This, s, directory, len_directory);
}
else
{
flags |= RDP_LOGON_BLOB;
DEBUG_RDP5(("Sending RDP5-style Logon packet\n"));
packetlen = 4 + /* Unknown uint32 */
4 + /* flags */
2 + /* len_domain */
2 + /* len_user */
(flags & RDP_LOGON_AUTO ? 2 : 0) + /* len_password */
(flags & RDP_LOGON_BLOB ? 2 : 0) + /* Length of BLOB */
2 + /* len_program */
2 + /* len_directory */
(0 < len_domain ? len_domain : 2) + /* domain */
len_user + (flags & RDP_LOGON_AUTO ? len_password : 0) + 0 + /* We have no 512 byte BLOB. Perhaps we must? */
(flags & RDP_LOGON_BLOB && !(flags & RDP_LOGON_AUTO) ? 2 : 0) + /* After the BLOB is a unknown int16. If there is a BLOB, that is. */
(0 < len_program ? len_program : 2) + (0 < len_directory ? len_directory : 2) + 2 + /* Unknown (2) */
2 + /* Client ip length */
len_ip + /* Client ip */
2 + /* DLL string length */
len_dll + /* DLL string */
2 + /* Unknown */
2 + /* Unknown */
64 + /* Time zone #0 */
2 + /* Unknown */
64 + /* Time zone #1 */
32; /* Unknown */
s = sec_init(This, sec_flags, packetlen);
DEBUG_RDP5(("Called sec_init with packetlen %d\n", packetlen));
if(s == NULL)
return False;
out_uint32(s, 0); /* Unknown */
out_uint32_le(s, flags);
out_uint16_le(s, len_domain);
out_uint16_le(s, len_user);
if (flags & RDP_LOGON_AUTO)
{
out_uint16_le(s, len_password);
}
if (flags & RDP_LOGON_BLOB && !(flags & RDP_LOGON_AUTO))
{
out_uint16_le(s, 0);
}
out_uint16_le(s, len_program);
out_uint16_le(s, len_directory);
if (0 < len_domain)
rdp_out_unistr(This, s, domain, len_domain);
else
out_uint16_le(s, 0);
rdp_out_unistr(This, s, user, len_user);
if (flags & RDP_LOGON_AUTO)
{
rdp_out_unistr(This, s, password, len_password);
}
if (flags & RDP_LOGON_BLOB && !(flags & RDP_LOGON_AUTO))
{
out_uint16_le(s, 0);
}
if (0 < len_program)
{
rdp_out_unistr(This, s, program, len_program);
}
else
{
out_uint16_le(s, 0);
}
if (0 < len_directory)
{
rdp_out_unistr(This, s, directory, len_directory);
}
else
{
out_uint16_le(s, 0);
}
out_uint16_le(s, 2);
out_uint16_le(s, len_ip + 2); /* Length of client ip */
rdp_out_unistr(This, s, ipaddr, len_ip);
out_uint16_le(s, len_dll + 2);
rdp_out_unistr(This, s, L"C:\\WINNT\\System32\\mstscax.dll", len_dll);
tzone = (mktime(gmtime(&t)) - mktime(localtime(&t))) / 60;
out_uint32_le(s, (uint32)tzone);
rdp_out_unistr(This, s, L"GTB, normaltid", 2 * (int)wcslen(L"GTB, normaltid"));
out_uint8s(s, 62 - 2 * wcslen(L"GTB, normaltid"));
out_uint32_le(s, 0x0a0000);
out_uint32_le(s, 0x050000);
out_uint32_le(s, 3);
out_uint32_le(s, 0);
out_uint32_le(s, 0);
rdp_out_unistr(This, s, L"GTB, sommartid", 2 * (int)wcslen(L"GTB, sommartid"));
out_uint8s(s, 62 - 2 * wcslen(L"GTB, sommartid"));
out_uint32_le(s, 0x30000);
out_uint32_le(s, 0x050000);
out_uint32_le(s, 2);
out_uint32(s, 0);
out_uint32_le(s, 0xffffffc4);
out_uint32_le(s, 0xfffffffe);
out_uint32_le(s, This->rdp5_performanceflags);
out_uint32(s, 0);
}
s_mark_end(s);
return sec_send(This, s, sec_flags);
}
/* Send a control PDU */
static BOOL
rdp_send_control(RDPCLIENT * This, uint16 action)
{
STREAM s;
s = rdp_init_data(This, 8);
if(s == NULL)
return False;
out_uint16_le(s, action);
out_uint16(s, 0); /* userid */
out_uint32(s, 0); /* control id */
s_mark_end(s);
return rdp_send_data(This, s, RDP_DATA_PDU_CONTROL);
}
/* Send a synchronisation PDU */
static BOOL
rdp_send_synchronise(RDPCLIENT * This)
{
STREAM s;
s = rdp_init_data(This, 4);
if(s == NULL)
return False;
out_uint16_le(s, 1); /* type */
out_uint16_le(s, 1002);
s_mark_end(s);
return rdp_send_data(This, s, RDP_DATA_PDU_SYNCHRONISE);
}
/* Send a single input event */
BOOL
rdp_send_input(RDPCLIENT * This, uint32 time, uint16 message_type, uint16 device_flags, uint16 param1, uint16 param2)
{
STREAM s;
s = rdp_init_data(This, 16);
if(s == NULL)
return False;
out_uint16_le(s, 1); /* number of events */
out_uint16(s, 0); /* pad */
out_uint32_le(s, time);
out_uint16_le(s, message_type);
out_uint16_le(s, device_flags);
out_uint16_le(s, param1);
out_uint16_le(s, param2);
s_mark_end(s);
return rdp_send_data(This, s, RDP_DATA_PDU_INPUT);
}
/* Send a client window information PDU */
BOOL
rdp_send_client_window_status(RDPCLIENT * This, int status)
{
STREAM s;
if (This->rdp.current_status == status)
return True;
s = rdp_init_data(This, 12);
if(s == NULL)
return False;
out_uint32_le(s, status);
switch (status)
{
case 0: /* shut the server up */
break;
case 1: /* receive data again */
out_uint32_le(s, 0); /* unknown */
out_uint16_le(s, This->width);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -