diff --git a/channels.c b/channels.c index d76fd1c..b5ea690 100644 --- a/channels.c +++ b/channels.c @@ -80,66 +80,101 @@ channel_init(VCHANNEL * channel, uint32 length) return s; } -void -channel_send(STREAM s, VCHANNEL * channel) +static void +channel_send_chunk(STREAM s, VCHANNEL * channel, uint32 length) { - uint32 length, flags; - uint32 thislength, remaining; - uint8 *data; + uint32 flags; + uint32 thislength; + RD_BOOL inplace; + STREAM chunk; -#ifdef WITH_SCARD - scard_lock(SCARD_LOCK_CHANNEL); -#endif - - /* first fragment sent in-place */ - s_pop_layer(s, channel_hdr); - length = s_remaining(s) - 8; - - logger(Protocol, Debug, "channel_send(), channel = %d, length = %d", channel->mcs_id, - length); - - thislength = MIN(length, vc_chunk_size); -/* 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. */ + /* 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. */ /* Actually, CHANNEL_CHUNK_LENGTH (default value is 1600 bytes) is described in MS-RDPBCGR (s. 2.2.6, s.3.1.5.2.1) and can be set by server only in the optional field VCChunkSize of VC Caps) */ - 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; + thislength = MIN(s_remaining(s), vc_chunk_size); - out_uint32_le(s, length); - out_uint32_le(s, flags); - data = s->end = s->p + thislength; - logger(Protocol, Debug, "channel_send(), sending %d bytes with FLAG_FIRST set", 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) + flags = 0; + if (length == s_remaining(s)) { - thislength = MIN(remaining, vc_chunk_size); - remaining -= thislength; - flags = (remaining == 0) ? CHANNEL_FLAG_LAST : 0; - if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL) - flags |= CHANNEL_FLAG_SHOW_PROTOCOL; + 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(), sending %d bytes with flags 0x%x", - thislength, flags); + logger(Protocol, Debug, "channel_send_chunk(), sending %d bytes with flags 0x%x", + thislength, flags); - s = sec_init(g_encryption ? SEC_ENCRYPT : 0, thislength + 8); - out_uint32_le(s, length); - out_uint32_le(s, flags); - out_uint8a(s, data, thislength); - s_mark_end(s); - sec_send_to_channel(s, g_encryption ? SEC_ENCRYPT : 0, channel->mcs_id); - s_free(s); + /* first fragment sent in-place */ + inplace = False; + if ((flags & (CHANNEL_FLAG_FIRST|CHANNEL_FLAG_LAST)) == + (CHANNEL_FLAG_FIRST|CHANNEL_FLAG_LAST)) + { + inplace = True; + } - data += thislength; + 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); + + /* Sending modifies the current offset, so make it is marked as + fully completed. */ + if (inplace) + { + in_uint8s(s, s_remaining(s)); + } + + if (!inplace) + { + s_free(chunk); + } +} + +void +channel_send(STREAM s, VCHANNEL * channel) +{ + uint32 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 diff --git a/stream.h b/stream.h index 45462e0..b9e16dc 100644 --- a/stream.h +++ b/stream.h @@ -138,6 +138,12 @@ size_t in_ansi_string(STREAM s, char *string, size_t len); /* Copy n bytes from array v in to STREAM s */ #define out_uint8a(s,v,n) { s_assert_w(s, n); memcpy((s)->p,v,n); (s)->p += n; } #define out_uint8s(s,n) { s_assert_w(s, n); memset((s)->p,0,n); (s)->p += n; } + +/* Copy n bytes from STREAM s in to STREAM v */ +#define in_uint8stream(s,v,n) { s_assert_r(s, n); out_uint8a((v), (s)->p, n); (s)->p += n; } +/* Copy n bytes in to STREAM s from STREAM v */ +#define out_uint8stream(s,v,n) in_uint8stream(v,s,n) +/* Copy the entire STREAM v (ignoring offsets) in to STREAM s */ #define out_stream(s, v) out_uint8a(s, (v)->data, s_length((v))) #define next_be(s,v) { s_assert_r(s, 1); v = ((v) << 8) + *((s)->p++); }