8cdc4aba7174ef5c96a6fc2f4c932947cd95e021
[platform/upstream/freerdp.git] / channels / remdesk / server / remdesk_main.c
1 /**
2  * FreeRDP: A Remote Desktop Protocol Implementation
3  * Remote Assistance Virtual Channel
4  *
5  * Copyright 2014 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 <winpr/crt.h>
25 #include <winpr/print.h>
26 #include <winpr/stream.h>
27
28 #include "remdesk_main.h"
29
30 int remdesk_virtual_channel_write(RemdeskServerContext* context, wStream* s)
31 {
32         BOOL status;
33         ULONG BytesWritten = 0;
34
35         status = WTSVirtualChannelWrite(context->priv->ChannelHandle,
36                         (PCHAR) Stream_Buffer(s), Stream_Length(s), &BytesWritten);
37
38         return (status) ? 1 : -1;
39 }
40
41 static int remdesk_read_channel_header(wStream* s, REMDESK_CHANNEL_HEADER* header)
42 {
43         int status;
44         UINT32 ChannelNameLen;
45         char* pChannelName = NULL;
46
47         if (Stream_GetRemainingLength(s) < 8)
48                 return -1;
49
50         Stream_Read_UINT32(s, ChannelNameLen); /* ChannelNameLen (4 bytes) */
51         Stream_Read_UINT32(s, header->DataLength); /* DataLen (4 bytes) */
52
53         if (ChannelNameLen > 64)
54                 return -1;
55
56         if ((ChannelNameLen % 2) != 0)
57                 return -1;
58
59         if (Stream_GetRemainingLength(s) < ChannelNameLen)
60                 return -1;
61
62         ZeroMemory(header->ChannelName, sizeof(header->ChannelName));
63
64         pChannelName = (char*) header->ChannelName;
65         status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(s),
66                         ChannelNameLen / 2, &pChannelName, 32, NULL, NULL);
67
68         Stream_Seek(s, ChannelNameLen);
69
70         if (status <= 0)
71                 return -1;
72
73         return 1;
74 }
75
76 static int remdesk_write_channel_header(wStream* s, REMDESK_CHANNEL_HEADER* header)
77 {
78         int index;
79         UINT32 ChannelNameLen;
80         WCHAR ChannelNameW[32];
81
82         ZeroMemory(ChannelNameW, sizeof(ChannelNameW));
83
84         for (index = 0; index < 32; index++)
85         {
86                 ChannelNameW[index] = (WCHAR) header->ChannelName[index];
87         }
88
89         ChannelNameLen = (strlen(header->ChannelName) + 1) * 2;
90
91         Stream_Write_UINT32(s, ChannelNameLen); /* ChannelNameLen (4 bytes) */
92         Stream_Write_UINT32(s, header->DataLength); /* DataLen (4 bytes) */
93
94         Stream_Write(s, ChannelNameW, ChannelNameLen); /* ChannelName (variable) */
95
96         return 1;
97 }
98
99 static int remdesk_write_ctl_header(wStream* s, REMDESK_CTL_HEADER* ctlHeader)
100 {
101         remdesk_write_channel_header(s, (REMDESK_CHANNEL_HEADER*) ctlHeader);
102         Stream_Write_UINT32(s, ctlHeader->msgType); /* msgType (4 bytes) */
103         return 1;
104 }
105
106 static int remdesk_prepare_ctl_header(REMDESK_CTL_HEADER* ctlHeader, UINT32 msgType, UINT32 msgSize)
107 {
108         ctlHeader->msgType = msgType;
109         strcpy(ctlHeader->ChannelName, REMDESK_CHANNEL_CTL_NAME);
110         ctlHeader->DataLength = 4 + msgSize;
111         return 1;
112 }
113
114 static int remdesk_send_ctl_result_pdu(RemdeskServerContext* context, UINT32 result)
115 {
116         wStream* s;
117         REMDESK_CTL_RESULT_PDU pdu;
118
119         pdu.result = result;
120
121         remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_RESULT, 4);
122
123         s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.DataLength);
124
125         remdesk_write_ctl_header(s, &(pdu.ctlHeader));
126
127         Stream_Write_UINT32(s, pdu.result); /* result (4 bytes) */
128
129         Stream_SealLength(s);
130
131         remdesk_virtual_channel_write(context, s);
132
133         Stream_Free(s, TRUE);
134
135         return 1;
136 }
137
138 static int remdesk_send_ctl_version_info_pdu(RemdeskServerContext* context)
139 {
140         wStream* s;
141         REMDESK_CTL_VERSION_INFO_PDU pdu;
142
143         remdesk_prepare_ctl_header(&(pdu.ctlHeader), REMDESK_CTL_VERSIONINFO, 8);
144
145         pdu.versionMajor = 1;
146         pdu.versionMinor = 2;
147
148         s = Stream_New(NULL, REMDESK_CHANNEL_CTL_SIZE + pdu.ctlHeader.DataLength);
149
150         remdesk_write_ctl_header(s, &(pdu.ctlHeader));
151
152         Stream_Write_UINT32(s, pdu.versionMajor); /* versionMajor (4 bytes) */
153         Stream_Write_UINT32(s, pdu.versionMinor); /* versionMinor (4 bytes) */
154
155         Stream_SealLength(s);
156
157         remdesk_virtual_channel_write(context, s);
158
159         return 1;
160 }
161
162 static int remdesk_recv_ctl_version_info_pdu(RemdeskServerContext* context, wStream* s, REMDESK_CHANNEL_HEADER* header)
163 {
164         UINT32 versionMajor;
165         UINT32 versionMinor;
166
167         if (Stream_GetRemainingLength(s) < 8)
168                 return -1;
169
170         Stream_Read_UINT32(s, versionMajor); /* versionMajor (4 bytes) */
171         Stream_Read_UINT32(s, versionMinor); /* versionMinor (4 bytes) */
172
173         return 1;
174 }
175
176 static int remdesk_recv_ctl_remote_control_desktop_pdu(RemdeskServerContext* context, wStream* s, REMDESK_CHANNEL_HEADER* header)
177 {
178         int status;
179         int cchStringW;
180         WCHAR* pStringW;
181         UINT32 msgLength;
182         int cbRaConnectionStringW = 0;
183         WCHAR* raConnectionStringW = NULL;
184         REMDESK_CTL_REMOTE_CONTROL_DESKTOP_PDU pdu;
185
186         msgLength = header->DataLength - 4;
187
188         pStringW = (WCHAR*) Stream_Pointer(s);
189         raConnectionStringW = pStringW;
190         cchStringW = 0;
191
192         while ((msgLength > 0) && pStringW[cchStringW])
193         {
194                 msgLength -= 2;
195                 cchStringW++;
196         }
197
198         if (pStringW[cchStringW] || !cchStringW)
199                 return -1;
200
201         cchStringW++;
202         cbRaConnectionStringW = cchStringW * 2;
203
204         pdu.raConnectionString = NULL;
205
206         status = ConvertFromUnicode(CP_UTF8, 0, raConnectionStringW,
207                         cbRaConnectionStringW / 2, &pdu.raConnectionString, 0, NULL, NULL);
208
209         if (status <= 0)
210                 return -1;
211
212         WLog_INFO(TAG, "RaConnectionString: %s",
213                           pdu.raConnectionString);
214         free(pdu.raConnectionString);
215
216         remdesk_send_ctl_result_pdu(context, 0);
217
218         return 1;
219 }
220
221 static int remdesk_recv_ctl_authenticate_pdu(RemdeskServerContext* context, wStream* s, REMDESK_CHANNEL_HEADER* header)
222 {
223         int status;
224         int cchStringW;
225         WCHAR* pStringW;
226         UINT32 msgLength;
227         int cbExpertBlobW = 0;
228         WCHAR* expertBlobW = NULL;
229         int cbRaConnectionStringW = 0;
230         WCHAR* raConnectionStringW = NULL;
231         REMDESK_CTL_AUTHENTICATE_PDU pdu;
232
233         msgLength = header->DataLength - 4;
234
235         pStringW = (WCHAR*) Stream_Pointer(s);
236         raConnectionStringW = pStringW;
237         cchStringW = 0;
238
239         while ((msgLength > 0) && pStringW[cchStringW])
240         {
241                 msgLength -= 2;
242                 cchStringW++;
243         }
244
245         if (pStringW[cchStringW] || !cchStringW)
246                 return -1;
247
248         cchStringW++;
249         cbRaConnectionStringW = cchStringW * 2;
250
251         pStringW += cchStringW;
252         expertBlobW = pStringW;
253         cchStringW = 0;
254
255         while ((msgLength > 0) && pStringW[cchStringW])
256         {
257                 msgLength -= 2;
258                 cchStringW++;
259         }
260
261         if (pStringW[cchStringW] || !cchStringW)
262                 return -1;
263
264         cchStringW++;
265         cbExpertBlobW = cchStringW * 2;
266
267         pdu.raConnectionString = NULL;
268
269         status = ConvertFromUnicode(CP_UTF8, 0, raConnectionStringW,
270                         cbRaConnectionStringW / 2, &pdu.raConnectionString, 0, NULL, NULL);
271
272         if (status <= 0)
273                 return -1;
274
275         pdu.expertBlob = NULL;
276
277         status = ConvertFromUnicode(CP_UTF8, 0, expertBlobW,
278                         cbExpertBlobW / 2, &pdu.expertBlob, 0, NULL, NULL);
279
280         if (status <= 0)
281                 return -1;
282
283         WLog_INFO(TAG, "RaConnectionString: %s ExpertBlob: %s",
284                           pdu.raConnectionString, pdu.expertBlob);
285         free(pdu.raConnectionString);
286         free(pdu.expertBlob);
287
288         return 1;
289 }
290
291 static int remdesk_recv_ctl_verify_password_pdu(RemdeskServerContext* context, wStream* s, REMDESK_CHANNEL_HEADER* header)
292 {
293         int status;
294         int cbExpertBlobW = 0;
295         WCHAR* expertBlobW = NULL;
296         REMDESK_CTL_VERIFY_PASSWORD_PDU pdu;
297
298         if (Stream_GetRemainingLength(s) < 8)
299                 return -1;
300
301         pdu.expertBlob = NULL;
302         expertBlobW = (WCHAR*) Stream_Pointer(s);
303         cbExpertBlobW = header->DataLength - 4;
304
305         status = ConvertFromUnicode(CP_UTF8, 0, expertBlobW, cbExpertBlobW / 2, &pdu.expertBlob, 0, NULL, NULL);
306         WLog_INFO(TAG, "ExpertBlob: %s", pdu.expertBlob);
307         remdesk_send_ctl_result_pdu(context, 0);
308
309         return 1;
310 }
311
312 static int remdesk_recv_ctl_pdu(RemdeskServerContext* context, wStream* s, REMDESK_CHANNEL_HEADER* header)
313 {
314         int status = 1;
315         UINT32 msgType = 0;
316
317         if (Stream_GetRemainingLength(s) < 4)
318                 return -1;
319
320         Stream_Read_UINT32(s, msgType); /* msgType (4 bytes) */
321         WLog_INFO(TAG, "msgType: %d", msgType);
322
323         switch (msgType)
324         {
325                 case REMDESK_CTL_REMOTE_CONTROL_DESKTOP:
326                         status = remdesk_recv_ctl_remote_control_desktop_pdu(context, s, header);
327                         break;
328
329                 case REMDESK_CTL_AUTHENTICATE:
330                         status = remdesk_recv_ctl_authenticate_pdu(context, s, header);
331                         break;
332
333                 case REMDESK_CTL_DISCONNECT:
334                         break;
335
336                 case REMDESK_CTL_VERSIONINFO:
337                         status = remdesk_recv_ctl_version_info_pdu(context, s, header);
338                         break;
339
340                 case REMDESK_CTL_ISCONNECTED:
341                         break;
342
343                 case REMDESK_CTL_VERIFY_PASSWORD:
344                         status = remdesk_recv_ctl_verify_password_pdu(context, s, header);
345                         break;
346
347                 case REMDESK_CTL_EXPERT_ON_VISTA:
348                         break;
349
350                 case REMDESK_CTL_RANOVICE_NAME:
351                         break;
352
353                 case REMDESK_CTL_RAEXPERT_NAME:
354                         break;
355
356                 case REMDESK_CTL_TOKEN:
357                         break;
358
359                 default:
360                         WLog_ERR(TAG, "remdesk_recv_control_pdu: unknown msgType: %d", msgType);
361                         status = -1;
362                         break;
363         }
364
365         return status;
366 }
367
368 static int remdesk_server_receive_pdu(RemdeskServerContext* context, wStream* s)
369 {
370         int status = 1;
371         REMDESK_CHANNEL_HEADER header;
372
373 #if 0
374         WLog_INFO(TAG, "RemdeskReceive: %d", Stream_GetRemainingLength(s));
375         winpr_HexDump(Stream_Pointer(s), Stream_GetRemainingLength(s));
376 #endif
377
378         if (remdesk_read_channel_header(s, &header) < 0)
379                 return -1;
380
381         if (strcmp(header.ChannelName, "RC_CTL") == 0)
382         {
383                 status = remdesk_recv_ctl_pdu(context, s, &header);
384         }
385         else if (strcmp(header.ChannelName, "70") == 0)
386         {
387
388         }
389         else if (strcmp(header.ChannelName, "71") == 0)
390         {
391
392         }
393         else if (strcmp(header.ChannelName, ".") == 0)
394         {
395
396         }
397         else if (strcmp(header.ChannelName, "1000.") == 0)
398         {
399
400         }
401         else if (strcmp(header.ChannelName, "RA_FX") == 0)
402         {
403
404         }
405         else
406         {
407
408         }
409
410         return 1;
411 }
412
413 static void* remdesk_server_thread(void* arg)
414 {
415         wStream* s;
416         DWORD status;
417         DWORD nCount;
418         void* buffer;
419         UINT32* pHeader;
420         UINT32 PduLength;
421         HANDLE events[8];
422         HANDLE ChannelEvent;
423         DWORD BytesReturned;
424         RemdeskServerContext* context;
425
426         context = (RemdeskServerContext*) arg;
427
428         buffer = NULL;
429         BytesReturned = 0;
430         ChannelEvent = NULL;
431
432         s = Stream_New(NULL, 4096);
433
434         if (WTSVirtualChannelQuery(context->priv->ChannelHandle, WTSVirtualEventHandle, &buffer, &BytesReturned) == TRUE)
435         {
436                 if (BytesReturned == sizeof(HANDLE))
437                         CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE));
438
439                 WTSFreeMemory(buffer);
440         }
441
442         nCount = 0;
443         events[nCount++] = ChannelEvent;
444         events[nCount++] = context->priv->StopEvent;
445
446         remdesk_send_ctl_version_info_pdu(context);
447
448         while (1)
449         {
450                 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
451
452                 if (WaitForSingleObject(context->priv->StopEvent, 0) == WAIT_OBJECT_0)
453                 {
454                         break;
455                 }
456
457                 if (WTSVirtualChannelRead(context->priv->ChannelHandle, 0,
458                                 (PCHAR) Stream_Buffer(s), Stream_Capacity(s), &BytesReturned))
459                 {
460                         if (BytesReturned)
461                                 Stream_Seek(s, BytesReturned);
462                 }
463                 else
464                 {
465                         Stream_EnsureRemainingCapacity(s, BytesReturned);
466                 }
467
468                 if (Stream_GetPosition(s) >= 8)
469                 {
470                         pHeader = (UINT32*) Stream_Buffer(s);
471                         PduLength = pHeader[0] + pHeader[1] + 8;
472
473                         if (PduLength >= Stream_GetPosition(s))
474                         {
475                                 Stream_SealLength(s);
476                                 Stream_SetPosition(s, 0);
477                                 remdesk_server_receive_pdu(context, s);
478                                 Stream_SetPosition(s, 0);
479                         }
480                 }
481         }
482
483         Stream_Free(s, TRUE);
484
485         return NULL;
486 }
487
488 static int remdesk_server_start(RemdeskServerContext* context)
489 {
490         context->priv->ChannelHandle = WTSVirtualChannelOpen(context->vcm, WTS_CURRENT_SESSION, "remdesk");
491
492         if (!context->priv->ChannelHandle)
493                 return -1;
494
495         context->priv->StopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
496
497         context->priv->Thread = CreateThread(NULL, 0,
498                         (LPTHREAD_START_ROUTINE) remdesk_server_thread, (void*) context, 0, NULL);
499
500         return 1;
501 }
502
503 static int remdesk_server_stop(RemdeskServerContext* context)
504 {
505         SetEvent(context->priv->StopEvent);
506
507         WaitForSingleObject(context->priv->Thread, INFINITE);
508         CloseHandle(context->priv->Thread);
509
510         return 1;
511 }
512
513 RemdeskServerContext* remdesk_server_context_new(HANDLE vcm)
514 {
515         RemdeskServerContext* context;
516
517         context = (RemdeskServerContext*) calloc(1, sizeof(RemdeskServerContext));
518
519         if (context)
520         {
521                 context->vcm = vcm;
522
523                 context->Start = remdesk_server_start;
524                 context->Stop = remdesk_server_stop;
525
526                 context->priv = (RemdeskServerPrivate*) calloc(1, sizeof(RemdeskServerPrivate));
527
528                 if (context->priv)
529                 {
530                         context->priv->Version = 1;
531                 }
532         }
533
534         return context;
535 }
536
537 void remdesk_server_context_free(RemdeskServerContext* context)
538 {
539         if (context)
540         {
541                 if (context->priv)
542                 {
543                         free(context->priv);
544                 }
545
546                 free(context);
547         }
548 }