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    Copyright 2016 Alexander Zakharov <uglym8@gmail.com>
7 
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "rdesktop.h"
23 
24 #define MAX_CHANNELS			6
25 #define CHANNEL_CHUNK_LENGTH		1600
26 #define CHANNEL_FLAG_FIRST		0x01
27 #define CHANNEL_FLAG_LAST		0x02
28 #define CHANNEL_FLAG_SHOW_PROTOCOL	0x10
29 
30 extern RDP_VERSION g_rdp_version;
31 extern RD_BOOL g_encryption;
32 
33 uint32 vc_chunk_size = CHANNEL_CHUNK_LENGTH;
34 
35 VCHANNEL g_channels[MAX_CHANNELS];
36 unsigned int g_num_channels;
37 
38 /* FIXME: We should use the information in TAG_SRV_CHANNELS to map RDP5
39    channels to MCS channels.
40 
41    The format of TAG_SRV_CHANNELS seems to be
42 
43    global_channel_no (uint16le)
44    number_of_other_channels (uint16le)
45    ..followed by uint16les for the other channels.
46 */
47 
48 VCHANNEL *
channel_register(char * name,uint32 flags,void (* callback)(STREAM))49 channel_register(char *name, uint32 flags, void (*callback) (STREAM))
50 {
51 	VCHANNEL *channel;
52 
53 	if (g_rdp_version < RDP_V5)
54 		return NULL;
55 
56 	if (g_num_channels >= MAX_CHANNELS)
57 	{
58 		logger(Core, Error,
59 		       "channel_register(), channel table full, increase MAX_CHANNELS");
60 		return NULL;
61 	}
62 
63 	channel = &g_channels[g_num_channels];
64 	channel->mcs_id = MCS_GLOBAL_CHANNEL + 1 + g_num_channels;
65 	strncpy(channel->name, name, 8);
66 	channel->flags = flags;
67 	channel->process = callback;
68 	g_num_channels++;
69 	return channel;
70 }
71 
72 STREAM
channel_init(VCHANNEL * channel,uint32 length)73 channel_init(VCHANNEL * channel, uint32 length)
74 {
75 	UNUSED(channel);
76 	STREAM s;
77 
78 	s = sec_init(g_encryption ? SEC_ENCRYPT : 0, length + 8);
79 	s_push_layer(s, channel_hdr, 8);
80 	return s;
81 }
82 
83 static void
channel_send_chunk(STREAM s,VCHANNEL * channel,uint32 length)84 channel_send_chunk(STREAM s, VCHANNEL * channel, uint32 length)
85 {
86 	uint32 flags;
87 	uint32 thislength;
88 	RD_BOOL inplace;
89 	STREAM chunk;
90 
91 	/* Note: In the original clipboard implementation, this number was
92 	   1592, not 1600. However, I don't remember the reason and 1600 seems
93 	   to work so.. This applies only to *this* length, not the length of
94 	   continuation or ending packets. */
95 
96 	/* Actually, CHANNEL_CHUNK_LENGTH (default value is 1600 bytes) is described
97 	   in MS-RDPBCGR (s. 2.2.6, s.3.1.5.2.1) and can be set by server only
98 	   in the optional field VCChunkSize of VC Caps) */
99 
100 	thislength = MIN(s_remaining(s), vc_chunk_size);
101 
102 	flags = 0;
103 	if (length == s_remaining(s))
104 	{
105 		flags |= CHANNEL_FLAG_FIRST;
106 	}
107 	if (s_remaining(s) == thislength)
108 	{
109 		flags |= CHANNEL_FLAG_LAST;
110 	}
111 	if (channel->flags & CHANNEL_OPTION_SHOW_PROTOCOL)
112 	{
113 		flags |= CHANNEL_FLAG_SHOW_PROTOCOL;
114 	}
115 
116 	logger(Protocol, Debug, "channel_send_chunk(), sending %d bytes with flags 0x%x",
117 	       thislength, flags);
118 
119 	/* first fragment sent in-place */
120 	inplace = False;
121 	if ((flags & (CHANNEL_FLAG_FIRST|CHANNEL_FLAG_LAST)) ==
122 	    (CHANNEL_FLAG_FIRST|CHANNEL_FLAG_LAST))
123 	{
124 		inplace = True;
125 	}
126 
127 	if (inplace)
128 	{
129 		s_pop_layer(s, channel_hdr);
130 		chunk = s;
131 	}
132 	else
133 	{
134 		chunk = sec_init(g_encryption ? SEC_ENCRYPT : 0, thislength + 8);
135 	}
136 
137 	out_uint32_le(chunk, length);
138 	out_uint32_le(chunk, flags);
139 	if (!inplace)
140 	{
141 		out_uint8stream(chunk, s, thislength);
142 		s_mark_end(chunk);
143 	}
144 	sec_send_to_channel(chunk, g_encryption ? SEC_ENCRYPT : 0, channel->mcs_id);
145 
146 	/* Sending modifies the current offset, so make it is marked as
147 	   fully completed. */
148 	if (inplace)
149 	{
150 		in_uint8s(s, s_remaining(s));
151 	}
152 
153 	if (!inplace)
154 	{
155 		s_free(chunk);
156 	}
157 }
158 
159 void
channel_send(STREAM s,VCHANNEL * channel)160 channel_send(STREAM s, VCHANNEL * channel)
161 {
162 	uint32 length;
163 
164 #ifdef WITH_SCARD
165 	scard_lock(SCARD_LOCK_CHANNEL);
166 #endif
167 
168 	s_pop_layer(s, channel_hdr);
169 	in_uint8s(s, 8);
170 	length = s_remaining(s);
171 
172 	logger(Protocol, Debug, "channel_send(), channel = %d, length = %d", channel->mcs_id,
173 	       length);
174 
175 	while (!s_check_end(s))
176 	{
177 		channel_send_chunk(s, channel, length);
178 	}
179 
180 #ifdef WITH_SCARD
181 	scard_unlock(SCARD_LOCK_CHANNEL);
182 #endif
183 }
184 
185 void
channel_process(STREAM s,uint16 mcs_channel)186 channel_process(STREAM s, uint16 mcs_channel)
187 {
188 	uint32 length, flags;
189 	uint32 thislength;
190 	VCHANNEL *channel = NULL;
191 	unsigned int i;
192 	STREAM in;
193 
194 	for (i = 0; i < g_num_channels; i++)
195 	{
196 		channel = &g_channels[i];
197 		if (channel->mcs_id == mcs_channel)
198 			break;
199 	}
200 
201 	if (i >= g_num_channels)
202 		return;
203 
204 	in_uint32_le(s, length);
205 	in_uint32_le(s, flags);
206 	if ((flags & CHANNEL_FLAG_FIRST) && (flags & CHANNEL_FLAG_LAST))
207 	{
208 		/* single fragment - pass straight up */
209 		channel->process(s);
210 	}
211 	else
212 	{
213 		/* add fragment to defragmentation buffer */
214 		in = &channel->in;
215 		if (flags & CHANNEL_FLAG_FIRST)
216 		{
217 			s_realloc(in, length);
218 			s_reset(in);
219 		}
220 
221 		thislength = s_remaining(s);
222 		out_uint8stream(in, s, thislength);
223 
224 		if (flags & CHANNEL_FLAG_LAST)
225 		{
226 			s_mark_end(in);
227 			s_seek(in, 0);
228 			channel->process(in);
229 		}
230 	}
231 }
232