/* -*- c-basic-offset: 8 -*- rdesktop: A Remote Desktop Protocol client. Protocol services - Virtual channels Copyright 2003 Erik Forsberg for Cendio AB Copyright (C) Matthew Chapman 2003-2008 Copyright 2016 Alexander Zakharov 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 . */ #include "rdesktop.h" #include #define MAX_CHANNELS 6 #define CHANNEL_CHUNK_LENGTH 1600 #define CHANNEL_FLAG_FIRST 0x01 #define CHANNEL_FLAG_LAST 0x02 #define CHANNEL_FLAG_SHOW_PROTOCOL 0x10 #define MIN(a, b) ((a) < (b) ? (a) : (b)) extern RDP_VERSION g_rdp_version; extern RD_BOOL g_encryption; uint32_t vc_chunk_size = CHANNEL_CHUNK_LENGTH; VCHANNEL g_channels[MAX_CHANNELS]; unsigned int g_num_channels; VCHANNEL *channel_register(const char *name, uint32_t flags, void (*callback) (STREAM)) { if (g_rdp_version < RDP_V5) { return NULL; } if (g_num_channels >= MAX_CHANNELS) { logger(Core, Error, "channel_register(), channel table full, increase MAX_CHANNELS"); return NULL; } VCHANNEL *channel = &g_channels[g_num_channels]; channel->mcs_id = MCS_GLOBAL_CHANNEL + 1 + g_num_channels; strncpy(channel->name, name, sizeof(channel->name) - 1); channel->name[sizeof(channel->name) - 1] = '\0'; // Ensure null-termination channel->flags = flags; channel->process = callback; g_num_channels++; return channel; } STREAM channel_init(VCHANNEL *channel, uint32_t length) { UNUSED(channel); STREAM s = sec_init(g_encryption ? SEC_ENCRYPT : 0, length + 8); s_push_layer(s, channel_hdr, 8); return s; } static void channel_send_chunk(STREAM s, VCHANNEL *channel, uint32_t length) { uint32_t flags; uint32_t thislength; RD_BOOL inplace; STREAM chunk; thislength = MIN(s_remaining(s), vc_chunk_size); flags = 0; if (length == s_remaining(s)) { flags |= CHANNEL_FLAG_FIRST; } if (s_remaining(s) == thislength) { flags |= CHANNEL_FLAG_LAST; } if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL) { flags |= CHANNEL_FLAG_SHOW_PROTOCOL; } logger(Protocol, Debug, "channel_send_chunk(), sending %d bytes with flags 0x%x", thislength, flags); inplace = (flags & (CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST)) == (CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST); if (inplace) { s_pop_layer(s, channel_hdr); chunk = s; } else { chunk = sec_init(g_encryption ? SEC_ENCRYPT : 0, thislength + 8); } out_uint32_le(chunk, length); out_uint32_le(chunk, flags); if (!inplace) { out_uint8stream(chunk, s, thislength); s_mark_end(chunk); } sec_send_to_channel(chunk, g_encryption ? SEC_ENCRYPT : 0, channel->mcs_id); if (inplace) { in_uint8s(s, s_remaining(s)); } else { s_free(chunk); } } void channel_send(STREAM s, VCHANNEL *channel) { uint32_t length; #ifdef WITH_SCARD scard_lock(SCARD_LOCK_CHANNEL); #endif s_pop_layer(s, channel_hdr); in_uint8s(s, 8); length = s_remaining(s); logger(Protocol, Debug, "channel_send(), channel = %d, length = %d", channel->mcs_id, length); while (!s_check_end(s)) { channel_send_chunk(s, channel, length); } #ifdef WITH_SCARD scard_unlock(SCARD_LOCK_CHANNEL); #endif } void channel_process(STREAM s, uint16_t mcs_channel) { uint32_t length, flags; uint32_t thislength; VCHANNEL *channel = NULL; unsigned int i; STREAM in; for (i = 0; i < g_num_channels; i++) { channel = &g_channels[i]; if (channel->mcs_id == mcs_channel) { break; } } if (i >= g_num_channels) { return; } in_uint32_le(s, length); in_uint32_le(s, flags); if ((flags & CHANNEL_FLAG_FIRST) && (flags & CHANNEL_FLAG_LAST)) { channel->process(s); } else { in = &channel->in; if (flags & CHANNEL_FLAG_FIRST) { s_realloc(in, length); s_reset(in); } thislength = s_remaining(s); out_uint8stream(in, s, thislength); if (flags & CHANNEL_FLAG_LAST) { s_mark_end(in); s_seek(in, 0); channel->process(in); } } }