xref: /reactos/base/applications/mstsc/channels.c (revision 5100859e)
1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    Protocol services - Virtual channels
4    Copyright 2003 Erik Forsberg <forsberg@cendio.se> for Cendio AB
5    Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 2003-2008
6 
7    This program is free software: you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation, either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "precomp.h"
22 
23 #define MAX_CHANNELS			6
24 #define CHANNEL_CHUNK_LENGTH		1600
25 #define CHANNEL_FLAG_FIRST		0x01
26 #define CHANNEL_FLAG_LAST		0x02
27 #define CHANNEL_FLAG_SHOW_PROTOCOL	0x10
28 
29 extern RDP_VERSION g_rdp_version;
30 extern RD_BOOL g_encryption;
31 
32 VCHANNEL g_channels[MAX_CHANNELS];
33 unsigned int g_num_channels;
34 
35 /* FIXME: We should use the information in TAG_SRV_CHANNELS to map RDP5
36    channels to MCS channels.
37 
38    The format of TAG_SRV_CHANNELS seems to be
39 
40    global_channel_no (uint16le)
41    number_of_other_channels (uint16le)
42    ..followed by uint16les for the other channels.
43 */
44 
45 VCHANNEL *
46 channel_register(char *name, uint32 flags, void (*callback) (STREAM))
47 {
48 	VCHANNEL *channel;
49 
50 	if (g_rdp_version < RDP_V5)
51 		return NULL;
52 
53 	if (g_num_channels >= MAX_CHANNELS)
54 	{
55 		error("Channel table full, increase MAX_CHANNELS\n");
56 		return NULL;
57 	}
58 
59 	channel = &g_channels[g_num_channels];
60 	channel->mcs_id = MCS_GLOBAL_CHANNEL + 1 + g_num_channels;
61 	strncpy(channel->name, name, 8);
62 	channel->flags = flags;
63 	channel->process = callback;
64 	g_num_channels++;
65 	return channel;
66 }
67 
68 STREAM
69 channel_init(VCHANNEL * channel, uint32 length)
70 {
71 	STREAM s;
72 
73 	s = sec_init(g_encryption ? SEC_ENCRYPT : 0, length + 8);
74 	s_push_layer(s, channel_hdr, 8);
75 	return s;
76 }
77 
78 void
79 channel_send(STREAM s, VCHANNEL * channel)
80 {
81 	uint32 length, flags;
82 	uint32 thislength, remaining;
83 	uint8 *data;
84 
85 #ifdef WITH_SCARD
86 	scard_lock(SCARD_LOCK_CHANNEL);
87 #endif
88 
89 	/* first fragment sent in-place */
90 	s_pop_layer(s, channel_hdr);
91 	length = s->end - s->p - 8;
92 
93 	DEBUG_CHANNEL(("channel_send, length = %d\n", length));
94 
95 	thislength = MIN(length, CHANNEL_CHUNK_LENGTH);
96 /* Note: In the original clipboard implementation, this number was
97    1592, not 1600. However, I don't remember the reason and 1600 seems
98    to work so.. This applies only to *this* length, not the length of
99    continuation or ending packets. */
100 	remaining = length - thislength;
101 	flags = (remaining == 0) ? CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST : CHANNEL_FLAG_FIRST;
102 	if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL)
103 		flags |= CHANNEL_FLAG_SHOW_PROTOCOL;
104 
105 	out_uint32_le(s, length);
106 	out_uint32_le(s, flags);
107 	data = s->end = s->p + thislength;
108 	DEBUG_CHANNEL(("Sending %d bytes with FLAG_FIRST\n", thislength));
109 	sec_send_to_channel(s, g_encryption ? SEC_ENCRYPT : 0, channel->mcs_id);
110 
111 	/* subsequent segments copied (otherwise would have to generate headers backwards) */
112 	while (remaining > 0)
113 	{
114 		thislength = MIN(remaining, CHANNEL_CHUNK_LENGTH);
115 		remaining -= thislength;
116 		flags = (remaining == 0) ? CHANNEL_FLAG_LAST : 0;
117 		if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL)
118 			flags |= CHANNEL_FLAG_SHOW_PROTOCOL;
119 
120 		DEBUG_CHANNEL(("Sending %d bytes with flags %d\n", thislength, flags));
121 
122 		s = sec_init(g_encryption ? SEC_ENCRYPT : 0, thislength + 8);
123 		out_uint32_le(s, length);
124 		out_uint32_le(s, flags);
125 		out_uint8p(s, data, thislength);
126 		s_mark_end(s);
127 		sec_send_to_channel(s, g_encryption ? SEC_ENCRYPT : 0, channel->mcs_id);
128 
129 		data += thislength;
130 	}
131 
132 #ifdef WITH_SCARD
133 	scard_unlock(SCARD_LOCK_CHANNEL);
134 #endif
135 }
136 
137 void
138 channel_process(STREAM s, uint16 mcs_channel)
139 {
140 	uint32 length, flags;
141 	uint32 thislength;
142 	VCHANNEL *channel = NULL;
143 	unsigned int i;
144 	STREAM in;
145 
146 	for (i = 0; i < g_num_channels; i++)
147 	{
148 		channel = &g_channels[i];
149 		if (channel->mcs_id == mcs_channel)
150 			break;
151 	}
152 
153 	if (i >= g_num_channels)
154 		return;
155 
156 	in_uint32_le(s, length);
157 	in_uint32_le(s, flags);
158 	if ((flags & CHANNEL_FLAG_FIRST) && (flags & CHANNEL_FLAG_LAST))
159 	{
160 		/* single fragment - pass straight up */
161 		channel->process(s);
162 	}
163 	else
164 	{
165 		/* add fragment to defragmentation buffer */
166 		in = &channel->in;
167 		if (flags & CHANNEL_FLAG_FIRST)
168 		{
169 			if (length > in->size)
170 			{
171 				in->data = (uint8 *) xrealloc(in->data, length);
172 				in->size = length;
173 			}
174 			in->p = in->data;
175 		}
176 
177 		thislength = MIN(s->end - s->p, in->data + in->size - in->p);
178 		memcpy(in->p, s->p, thislength);
179 		in->p += thislength;
180 
181 		if (flags & CHANNEL_FLAG_LAST)
182 		{
183 			in->end = in->p;
184 			in->p = in->data;
185 			channel->process(in);
186 		}
187 	}
188 }
189