1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * X.224 Transport Protocol Data Units (TPDUs)
4  *
5  * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <stdio.h>
25 #include <winpr/print.h>
26 
27 #include <freerdp/log.h>
28 
29 #include "tpdu.h"
30 
31 #define TAG FREERDP_TAG("core")
32 
33 /**
34  * TPDUs are defined in:
35  *
36  * http://www.itu.int/rec/T-REC-X.224-199511-I/
37  * X.224: Information technology - Open Systems Interconnection - Protocol for providing the
38  * connection-mode transport service
39  *
40  * RDP uses only TPDUs of class 0, the "simple class" defined in section 8 of X.224
41  *
42  *       TPDU Header
43  *  ____________________   byte
44  * |                    |
45  * |         LI         |   1
46  * |____________________|
47  * |                    |
48  * |        Code        |   2
49  * |____________________|
50  * |                    |
51  * |                    |   3
52  * |_______DST-REF______|
53  * |                    |
54  * |                    |   4
55  * |____________________|
56  * |                    |
57  * |                    |   5
58  * |_______SRC-REF______|
59  * |                    |
60  * |                    |   6
61  * |____________________|
62  * |                    |
63  * |        Class       |   7
64  * |____________________|
65  * |         ...        |
66  */
67 
68 static void tpdu_write_header(wStream* s, UINT16 length, BYTE code);
69 
70 /**
71  * Read TPDU header.
72  * @param s stream
73  * @param code variable pointer to receive TPDU code
74  * @return TPDU length indicator (LI)
75  */
76 
tpdu_read_header(wStream * s,BYTE * code,BYTE * li,UINT16 tpktlength)77 BOOL tpdu_read_header(wStream* s, BYTE* code, BYTE* li, UINT16 tpktlength)
78 {
79 	if (Stream_GetRemainingLength(s) < 3)
80 		return FALSE;
81 
82 	Stream_Read_UINT8(s, *li);   /* LI */
83 	Stream_Read_UINT8(s, *code); /* Code */
84 
85 	if (*li + 4 > tpktlength)
86 	{
87 		WLog_ERR(TAG, "tpdu length %" PRIu8 " > tpkt header length %" PRIu16, *li, tpktlength);
88 		return FALSE;
89 	}
90 
91 	if (*code == X224_TPDU_DATA)
92 	{
93 		/* EOT (1 byte) */
94 		Stream_Seek(s, 1);
95 	}
96 	else
97 	{
98 		/* DST-REF (2 bytes) */
99 		/* SRC-REF (2 bytes) */
100 		/* Class 0 (1 byte) */
101 		return Stream_SafeSeek(s, 5);
102 	}
103 
104 	return TRUE;
105 }
106 
107 /**
108  * Write TDPU header.
109  * @param s stream
110  * @param length length
111  * @param code TPDU code
112  */
113 
tpdu_write_header(wStream * s,UINT16 length,BYTE code)114 void tpdu_write_header(wStream* s, UINT16 length, BYTE code)
115 {
116 	Stream_Write_UINT8(s, length); /* LI */
117 	Stream_Write_UINT8(s, code);   /* code */
118 
119 	if (code == X224_TPDU_DATA)
120 	{
121 		Stream_Write_UINT8(s, 0x80); /* EOT */
122 	}
123 	else
124 	{
125 		Stream_Write_UINT16(s, 0); /* DST-REF */
126 		Stream_Write_UINT16(s, 0); /* SRC-REF */
127 		Stream_Write_UINT8(s, 0);  /* Class 0 */
128 	}
129 }
130 
131 /**
132  * Read Connection Request TPDU
133  * @param s stream
134  * @return length indicator (LI)
135  */
136 
tpdu_read_connection_request(wStream * s,BYTE * li,UINT16 tpktlength)137 BOOL tpdu_read_connection_request(wStream* s, BYTE* li, UINT16 tpktlength)
138 {
139 	BYTE code;
140 
141 	if (!tpdu_read_header(s, &code, li, tpktlength))
142 		return FALSE;
143 
144 	if (code != X224_TPDU_CONNECTION_REQUEST)
145 	{
146 		WLog_ERR(TAG, "Error: expected X224_TPDU_CONNECTION_REQUEST");
147 		return FALSE;
148 	}
149 
150 	return TRUE;
151 }
152 
153 /**
154  * Write Connection Request TPDU.
155  * @param s stream
156  * @param length TPDU length
157  */
158 
tpdu_write_connection_request(wStream * s,UINT16 length)159 void tpdu_write_connection_request(wStream* s, UINT16 length)
160 {
161 	tpdu_write_header(s, length, X224_TPDU_CONNECTION_REQUEST);
162 }
163 
164 /**
165  * Read Connection Confirm TPDU.
166  * @param s stream
167  * @return length indicator (LI)
168  */
169 
tpdu_read_connection_confirm(wStream * s,BYTE * li,UINT16 tpktlength)170 BOOL tpdu_read_connection_confirm(wStream* s, BYTE* li, UINT16 tpktlength)
171 {
172 	BYTE code;
173 	size_t position;
174 	size_t bytes_read = 0;
175 
176 	/* save the position to determine the number of bytes read */
177 	position = Stream_GetPosition(s);
178 
179 	if (!tpdu_read_header(s, &code, li, tpktlength))
180 		return FALSE;
181 
182 	if (code != X224_TPDU_CONNECTION_CONFIRM)
183 	{
184 		WLog_ERR(TAG, "Error: expected X224_TPDU_CONNECTION_CONFIRM");
185 		return FALSE;
186 	}
187 	/*
188 	 * To ensure that there are enough bytes remaining for processing
189 	 * check against the length indicator (li). Already read bytes need
190 	 * to be taken into account.
191 	 * The -1 is because li was read but isn't included in the TPDU size.
192 	 * For reference see ITU-T Rec. X.224 - 13.2.1
193 	 */
194 	bytes_read = (Stream_GetPosition(s) - position) - 1;
195 
196 	return (Stream_GetRemainingLength(s) >= (size_t)(*li - bytes_read));
197 }
198 
199 /**
200  * Write Connection Confirm TPDU.
201  * @param s stream
202  * @param length TPDU length
203  */
204 
tpdu_write_connection_confirm(wStream * s,UINT16 length)205 void tpdu_write_connection_confirm(wStream* s, UINT16 length)
206 {
207 	tpdu_write_header(s, length, X224_TPDU_CONNECTION_CONFIRM);
208 }
209 
210 /**
211  * Write Disconnect Request TPDU.
212  * @param s stream
213  * @param length TPDU length
214  */
215 
tpdu_write_disconnect_request(wStream * s,UINT16 length)216 void tpdu_write_disconnect_request(wStream* s, UINT16 length)
217 {
218 	tpdu_write_header(s, length, X224_TPDU_DISCONNECT_REQUEST);
219 }
220 
221 /**
222  * Write Data TPDU.
223  * @param s stream
224  */
225 
tpdu_write_data(wStream * s)226 void tpdu_write_data(wStream* s)
227 {
228 	tpdu_write_header(s, 2, X224_TPDU_DATA);
229 }
230 
231 /**
232  * Read Data TPDU.
233  * @param s stream
234  */
235 
tpdu_read_data(wStream * s,UINT16 * LI,UINT16 tpktlength)236 BOOL tpdu_read_data(wStream* s, UINT16* LI, UINT16 tpktlength)
237 {
238 	BYTE code;
239 	BYTE li;
240 
241 	if (!tpdu_read_header(s, &code, &li, tpktlength))
242 		return FALSE;
243 
244 	if (code != X224_TPDU_DATA)
245 		return FALSE;
246 
247 	*LI = li;
248 
249 	return TRUE;
250 }
251