--- sourceforge.net/trunk/rdesktop/channels.c 2003/06/06 10:10:19 400 +++ sourceforge.net/trunk/rdesktop/channels.c 2004/06/15 22:45:12 711 @@ -1,7 +1,8 @@ /* -*- c-basic-offset: 8 -*- rdesktop: A Remote Desktop Protocol client. - Protocol services - Channel register + Protocol services - Virtual channels Copyright (C) Erik Forsberg 2003 + Copyright (C) Matthew Chapman 2003 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 @@ -20,73 +21,161 @@ #include "rdesktop.h" -static uint16 num_channels; -static rdp5_channel *channels[MAX_RDP5_CHANNELS]; +#define MAX_CHANNELS 4 +#define CHANNEL_CHUNK_LENGTH 1600 +#define CHANNEL_FLAG_FIRST 0x01 +#define CHANNEL_FLAG_LAST 0x02 +#define CHANNEL_FLAG_SHOW_PROTOCOL 0x10 + +extern BOOL g_use_rdp5; +extern BOOL g_encryption; + +VCHANNEL g_channels[MAX_CHANNELS]; +unsigned int g_num_channels; + +/* FIXME: We should use the information in TAG_SRV_CHANNELS to map RDP5 + channels to MCS channels. + + The format of TAG_SRV_CHANNELS seems to be + + global_channel_no (uint16le) + number_of_other_channels (uint16le) + ..followed by uint16les for the other channels. +*/ -uint16 -get_num_channels(void) +VCHANNEL * +channel_register(char *name, uint32 flags, void (*callback) (STREAM)) { - return num_channels; + VCHANNEL *channel; + + if (!g_use_rdp5) + return NULL; + + if (g_num_channels >= MAX_CHANNELS) + { + error("Channel table full, increase MAX_CHANNELS\n"); + return NULL; + } + + channel = &g_channels[g_num_channels]; + channel->mcs_id = MCS_GLOBAL_CHANNEL + 1 + g_num_channels; + strncpy(channel->name, name, 8); + channel->flags = flags; + channel->process = callback; + g_num_channels++; + return channel; +} + +STREAM +channel_init(VCHANNEL * channel, uint32 length) +{ + STREAM s; + + s = sec_init(g_encryption ? SEC_ENCRYPT : 0, length + 8); + s_push_layer(s, channel_hdr, 8); + return s; } void -register_channel(char *name, uint32 flags, void (*callback) (STREAM, uint16)) +channel_send(STREAM s, VCHANNEL * channel) { - if (num_channels > MAX_RDP5_CHANNELS) + uint32 length, flags; + uint32 thislength, remaining; + uint8 *data; + + /* first fragment sent in-place */ + s_pop_layer(s, channel_hdr); + length = s->end - s->p - 8; + + DEBUG_CLIPBOARD(("channel_send, length = %d\n", length)); + + thislength = MIN(length, CHANNEL_CHUNK_LENGTH); +/* Note: In the original clipboard implementation, this number was + 1592, not 1600. However, I don't remember the reason and 1600 seems + to work so.. This applies only to *this* length, not the length of + continuation or ending packets. */ + remaining = length - thislength; + flags = (remaining == 0) ? CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST : CHANNEL_FLAG_FIRST; + if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL) + flags |= CHANNEL_FLAG_SHOW_PROTOCOL; + + out_uint32_le(s, length); + out_uint32_le(s, flags); + data = s->end = s->p + thislength; + DEBUG_CLIPBOARD(("Sending %d bytes with FLAG_FIRST\n", thislength)); + sec_send_to_channel(s, g_encryption ? SEC_ENCRYPT : 0, channel->mcs_id); + + /* subsequent segments copied (otherwise would have to generate headers backwards) */ + while (remaining > 0) { - error("Maximum number of RDP5 channels reached. Redefine MAX_RDP5_CHANNELS in constants.h and recompile!\n!"); + thislength = MIN(remaining, CHANNEL_CHUNK_LENGTH); + remaining -= thislength; + flags = (remaining == 0) ? CHANNEL_FLAG_LAST : 0; + if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL) + flags |= CHANNEL_FLAG_SHOW_PROTOCOL; + + DEBUG_CLIPBOARD(("Sending %d bytes with flags %d\n", thislength, flags)); + + s = sec_init(g_encryption ? SEC_ENCRYPT : 0, thislength + 8); + out_uint32_le(s, length); + out_uint32_le(s, flags); + out_uint8p(s, data, thislength); + s_mark_end(s); + sec_send_to_channel(s, g_encryption ? SEC_ENCRYPT : 0, channel->mcs_id); + + data += thislength; } - num_channels++; - channels[num_channels - 1] = xrealloc(channels[num_channels - 1], - sizeof(rdp5_channel) * num_channels); - channels[num_channels - 1]->channelno = MCS_GLOBAL_CHANNEL + num_channels; - strcpy(channels[num_channels - 1]->name, name); - channels[num_channels - 1]->channelflags = flags; - channels[num_channels - 1]->channelcallback = callback; } -rdp5_channel * -find_channel_by_channelno(uint16 channelno) +void +channel_process(STREAM s, uint16 mcs_channel) { - if (channelno > MCS_GLOBAL_CHANNEL + num_channels) - { - warning("Channel %d not defined. Highest channel defined is %d\n", - channelno, MCS_GLOBAL_CHANNEL + num_channels); - return NULL; - } - else + uint32 length, flags; + uint32 thislength; + VCHANNEL *channel = NULL; + unsigned int i; + STREAM in; + + for (i = 0; i < g_num_channels; i++) { - return channels[channelno - MCS_GLOBAL_CHANNEL - 1]; + channel = &g_channels[i]; + if (channel->mcs_id == mcs_channel) + break; } -} -rdp5_channel * -find_channel_by_num(uint16 num) -{ - if (num > num_channels) + 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)) { - error("There are only %d channels defined, channel %d doesn't exist\n", - num_channels, num); + /* single fragment - pass straight up */ + channel->process(s); } else { - return channels[num]; + /* add fragment to defragmentation buffer */ + in = &channel->in; + if (flags & CHANNEL_FLAG_FIRST) + { + if (length > in->size) + { + in->data = (uint8*)xrealloc(in->data, length); + in->size = length; + } + in->p = in->data; + } + + thislength = MIN(s->end - s->p, in->data + in->size - in->p); + memcpy(in->p, s->p, thislength); + in->p += thislength; + + if (flags & CHANNEL_FLAG_LAST) + { + in->end = in->p; + in->p = in->data; + channel->process(in); + } } - return NULL; // Shut the compiler up -} - - - -void -dummy_callback(STREAM s, uint16 channelno) -{ - warning("Server is sending information on our dummy channel (%d). Why?\n", channelno); -} - -void -channels_init(void) -{ - DEBUG_RDP5(("channels_init\n")); - register_channel("dummych", 0xc0a0, dummy_callback); - register_channel("cliprdr", 0xc0a0, cliprdr_callback); }