VirtualBox

source: vbox/trunk/src/VBox/RDP/client-1.8.3/ctrl.c@ 74930

Last change on this file since 74930 was 55123, checked in by vboxsync, 10 years ago

rdesktop 1.8.3 modified for VBox

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.0 KB
Line 
1/* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Master/Slave remote controlling
4 Copyright 2013 Henrik Andersson <[email protected]> for Cendio AB
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20/*
21 * Oracle GPL Disclaimer: For the avoidance of doubt, except that if any license choice
22 * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
23 * the General Public License version 2 (GPLv2) at this time for any software where
24 * a choice of GPL license versions is made available with the language indicating
25 * that GPLv2 or any later version may be used, or where a choice of which version
26 * of the GPL is applied is otherwise unspecified.
27 */
28
29#include "rdesktop.h"
30#include "ssl.h"
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <sys/types.h>
35#include <sys/socket.h>
36#include <sys/stat.h>
37#include <sys/un.h>
38#include <limits.h>
39#include <unistd.h>
40
41#define CTRL_LINEBUF_SIZE 1024
42#define CTRL_RESULT_SIZE 32
43#define RDESKTOP_CTRLSOCK_STORE "/.local/share/rdesktop/ctrl"
44
45#define CTRL_HASH_FLAG_SEAMLESS 1
46
47#define ERR_RESULT_OK 0x00
48#define ERR_RESULT_NO_SUCH_COMMAND 0xffffffff
49
50extern RD_BOOL g_seamless_rdp;
51extern uint8 g_static_rdesktop_salt_16[];
52
53static RD_BOOL _ctrl_is_slave;
54static int ctrlsock;
55static char ctrlsock_name[PATH_MAX];
56static struct _ctrl_slave_t *_ctrl_slaves;
57
58#define CMD_SEAMLESS_SPAWN "seamless.spawn"
59
60typedef struct _ctrl_slave_t
61{
62 struct _ctrl_slave_t *prev, *next;
63 int sock;
64 char linebuf[CTRL_LINEBUF_SIZE];
65} _ctrl_slave_t;
66
67
68static void
69_ctrl_slave_new(int sock)
70{
71 _ctrl_slave_t *it, *ns;
72
73 /* initialize new slave list item */
74 ns = (_ctrl_slave_t *) xmalloc(sizeof(_ctrl_slave_t));
75 memset(ns, 0, sizeof(_ctrl_slave_t));
76 ns->sock = sock;
77
78 /* append new slave to end of list */
79 it = _ctrl_slaves;
80
81 /* find last element in list */
82 while (it && it->next)
83 it = it->next;
84
85 /* if last found append new */
86 if (it)
87 {
88 it->next = ns;
89 ns->prev = it;
90 }
91 else
92 {
93 /* no elemnts in list, lets add first */
94 _ctrl_slaves = ns;
95 }
96}
97
98static void
99_ctrl_slave_disconnect(int sock)
100{
101 _ctrl_slave_t *it;
102
103 if (!_ctrl_slaves)
104 return;
105
106 it = _ctrl_slaves;
107
108 /* find slave with sock */
109 while (it->next && it->sock != sock)
110 it = it->next;
111
112 if (it->sock == sock)
113 {
114 /* shutdown socket */
115 shutdown(sock, SHUT_RDWR);
116 close(sock);
117
118 /* remove item from list */
119 if (it == _ctrl_slaves)
120 {
121 if (it->next)
122 _ctrl_slaves = it->next;
123 else
124 _ctrl_slaves = NULL;
125 }
126
127 if (it->prev)
128 {
129 (it->prev)->next = it->next;
130 if (it->next)
131 (it->next)->prev = it->prev;
132 }
133 else if (it->next)
134 (it->next)->prev = NULL;
135
136 xfree(it);
137
138 }
139}
140
141static void
142_ctrl_command_result(_ctrl_slave_t * slave, int result)
143{
144 char buf[64] = { 0 };
145
146 /* translate and send result code back to client */
147 if (result == 0)
148 send(slave->sock, "OK\n", 3, 0);
149 else
150 {
151 snprintf(buf, 64, "ERROR %x\n", result);
152 send(slave->sock, buf, strlen(buf), 0);
153 }
154}
155
156static void
157_ctrl_dispatch_command(_ctrl_slave_t * slave)
158{
159 char *p;
160 char *cmd;
161 unsigned int res;
162
163 /* unescape linebuffer */
164 cmd = utils_string_unescape(slave->linebuf);
165 if (strncmp(cmd, CMD_SEAMLESS_SPAWN " ", strlen(CMD_SEAMLESS_SPAWN) + 1) == 0)
166 {
167 /* process seamless spawn request */
168 p = strstr(cmd, "seamlessrdpshell.exe");
169 if (p)
170 p += strlen("seamlessrdpshell.exe") + 1;
171 else
172 p = cmd + strlen(CMD_SEAMLESS_SPAWN) + 1;
173
174 res = ERR_RESULT_OK;
175
176 if (seamless_send_spawn(p) == (unsigned int) -1)
177 res = 1;
178 }
179 else
180 {
181 res = ERR_RESULT_NO_SUCH_COMMAND;
182 }
183 xfree(cmd);
184
185 _ctrl_command_result(slave, res);
186}
187
188static RD_BOOL
189_ctrl_verify_unix_socket()
190{
191 int s, len;
192 struct sockaddr_un saun;
193
194 memset(&saun, 0, sizeof(struct sockaddr_un));
195
196 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
197 {
198 perror("Error creating ctrl client socket: socket()");
199 exit(1);
200 }
201
202 saun.sun_family = AF_UNIX;
203 strcpy(saun.sun_path, ctrlsock_name);
204 len = sizeof(saun.sun_family) + strlen(saun.sun_path);
205
206 /* test connection */
207 if (connect(s, (struct sockaddr *) &saun, len) != 0)
208 return False;
209
210 shutdown(s, SHUT_RDWR);
211 close(s);
212 return True;
213}
214
215
216static void
217_ctrl_create_hash(const char *user, const char *domain, const char *host, char *hash, size_t hsize)
218{
219 RDSSL_SHA1 sha1;
220 uint8 out[20], delim;
221 uint16 version;
222 uint32 flags;
223
224 /* version\0user\0domain\0host\0flags */
225 flags = 0;
226 delim = '\0';
227 version = 0x0100;
228
229 if (g_seamless_rdp)
230 flags = CTRL_HASH_FLAG_SEAMLESS;
231
232 rdssl_sha1_init(&sha1);
233 rdssl_sha1_update(&sha1, (uint8 *) & version, sizeof(version));
234 rdssl_sha1_update(&sha1, &delim, 1);
235
236 if (user)
237 rdssl_sha1_update(&sha1, (uint8 *) user, strlen(user));
238 rdssl_sha1_update(&sha1, &delim, 1);
239
240 if (domain)
241 rdssl_sha1_update(&sha1, (uint8 *) domain, strlen(domain));
242 rdssl_sha1_update(&sha1, &delim, 1);
243
244 if (host)
245 rdssl_sha1_update(&sha1, (uint8 *) host, strlen(host));
246 rdssl_sha1_update(&sha1, &delim, 1);
247
248 rdssl_sha1_update(&sha1, (uint8 *) & flags, sizeof(flags));
249 rdssl_sha1_final(&sha1, out);
250
251 sec_hash_to_string(hash, hsize, out, sizeof(out));
252}
253
254
255/** Initialize ctrl
256 Ret values: <0 failure, 0 master, 1 client
257 */
258int
259ctrl_init(const char *user, const char *domain, const char *host)
260{
261 struct stat st;
262 struct sockaddr_un saun;
263 char hash[41], path[PATH_MAX];
264 char *home;
265
266 /* check if ctrl already initialized */
267 if (ctrlsock != 0 || _ctrl_is_slave)
268 return 0;
269
270 home = getenv("HOME");
271 if (home == NULL)
272 {
273 return -1;
274 }
275
276 /* get uniq hash for ctrlsock name */
277 _ctrl_create_hash(user, domain, host, hash, 41);
278 snprintf(ctrlsock_name, PATH_MAX, "%s" RDESKTOP_CTRLSOCK_STORE "/%s.ctl", home, hash);
279 ctrlsock_name[sizeof(ctrlsock_name) - 1] = '\0';
280
281 /* make sure that ctrlsock store path exists */
282 snprintf(path, PATH_MAX, "%s" RDESKTOP_CTRLSOCK_STORE, home);
283 path[sizeof(path) - 1] = '\0';
284 if (utils_mkdir_p(path, 0700) == -1)
285 {
286 perror(path);
287 return -1;
288 }
289
290 /* check if ctrl socket already exist then this process becomes a client */
291 if (stat(ctrlsock_name, &st) == 0)
292 {
293 /* verify that unix socket is not stale */
294 if (_ctrl_verify_unix_socket() == True)
295 {
296 _ctrl_is_slave = True;
297 return 1;
298 }
299 else
300 {
301 unlink(ctrlsock_name);
302 }
303 }
304
305 /* setup ctrl socket and start listening for connections */
306 if ((ctrlsock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
307 {
308 perror("Error creating ctrl socket:");
309 exit(1);
310 }
311
312 /* bind and start listening on server socket */
313 memset(&saun, 0, sizeof(struct sockaddr_un));
314 saun.sun_family = AF_UNIX;
315 strncpy(saun.sun_path, ctrlsock_name, sizeof(saun.sun_path));
316 if (bind(ctrlsock, (struct sockaddr *) &saun, sizeof(struct sockaddr_un)) < 0)
317 {
318 perror("Error binding ctrl socket:");
319 exit(1);
320 }
321
322 if (listen(ctrlsock, 5) < 0)
323 {
324 perror("Error listening on socket:");
325 exit(1);
326 }
327
328 /* add ctrl cleanup func to exit hooks */
329 atexit(ctrl_cleanup);
330
331 return 0;
332}
333
334void
335ctrl_cleanup()
336{
337 if (ctrlsock)
338 {
339 close(ctrlsock);
340 unlink(ctrlsock_name);
341 }
342}
343
344RD_BOOL
345ctrl_is_slave()
346{
347 return _ctrl_is_slave;
348}
349
350
351void
352ctrl_add_fds(int *n, fd_set * rfds)
353{
354 _ctrl_slave_t *it;
355 if (ctrlsock == 0)
356 return;
357
358 FD_SET(ctrlsock, rfds);
359 *n = MAX(*n, ctrlsock);
360
361
362 /* add connected slaves to fd set */
363 it = _ctrl_slaves;
364 while (it)
365 {
366 FD_SET(it->sock, rfds);
367 *n = MAX(*n, it->sock);
368 it = it->next;
369 }
370}
371
372void
373ctrl_check_fds(fd_set * rfds, fd_set * wfds)
374{
375 int ns, res, offs;
376 struct sockaddr_un fsaun;
377 socklen_t fromlen;
378 _ctrl_slave_t *it;
379
380 if (ctrlsock == 0)
381 return;
382
383 memset(&fsaun, 0, sizeof(struct sockaddr_un));
384
385 /* check if we got any connections on server socket */
386 if (FD_ISSET(ctrlsock, rfds))
387 {
388 FD_CLR(ctrlsock, rfds);
389 fromlen = sizeof(fsaun);
390 ns = accept(ctrlsock, (struct sockaddr *) &fsaun, &fromlen);
391 if (ns < 0)
392 {
393 perror("server: accept()");
394 exit(1);
395 }
396
397 _ctrl_slave_new(ns);
398 return;
399 }
400
401 /* check if any of our slaves fds has data */
402 it = _ctrl_slaves;
403 while (it)
404 {
405 if (FD_ISSET(it->sock, rfds))
406 {
407 offs = strlen(it->linebuf);
408 res = recv(it->sock, it->linebuf + offs, CTRL_LINEBUF_SIZE - offs, 0);
409 FD_CLR(it->sock, rfds);
410
411 /* linebuffer full let's disconnect slave */
412 if (it->linebuf[CTRL_LINEBUF_SIZE - 1] != '\0' &&
413 it->linebuf[CTRL_LINEBUF_SIZE - 1] != '\n')
414 {
415 _ctrl_slave_disconnect(it->sock);
416 break;
417 }
418
419 if (res > 0)
420 {
421 /* Check if we got full command line */
422 char *p;
423 if ((p = strchr(it->linebuf, '\n')) == NULL)
424 continue;
425
426 /* iterate over string and check against escaped \n */
427 while (p)
428 {
429 /* Check if newline is escaped */
430 if (p > it->linebuf && *(p - 1) != '\\')
431 break;
432 p = strchr(p + 1, '\n');
433 }
434
435 /* If we havent found an nonescaped \n we need more data */
436 if (p == NULL)
437 continue;
438
439 /* strip new linebuf and dispatch command */
440 *p = '\0';
441 _ctrl_dispatch_command(it);
442 memset(it->linebuf, 0, CTRL_LINEBUF_SIZE);
443 }
444 else
445 {
446 /* Peer disconnected or socket error */
447 _ctrl_slave_disconnect(it->sock);
448 break;
449 }
450 }
451 it = it->next;
452 }
453}
454
455#if HAVE_ICONV
456extern char g_codepage[16];
457#endif
458
459int
460ctrl_send_command(const char *cmd, const char *arg)
461{
462 FILE *fp;
463 struct sockaddr_un saun;
464 int s, len, index, ret;
465 char data[CTRL_LINEBUF_SIZE], tmp[CTRL_LINEBUF_SIZE];
466 char result[CTRL_RESULT_SIZE], c, *escaped;
467
468 escaped = NULL;
469
470 if (!_ctrl_is_slave)
471 return -1;
472
473 if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
474 {
475 perror("Error creating ctrl client socket: socket()");
476 exit(1);
477 }
478
479 memset(&saun, 0, sizeof(struct sockaddr_un));
480 saun.sun_family = AF_UNIX;
481 strcpy(saun.sun_path, ctrlsock_name);
482 len = sizeof(saun.sun_family) + strlen(saun.sun_path);
483
484 if (connect(s, (struct sockaddr *) &saun, len) < 0)
485 {
486 perror("Error connecting to ctrl socket: connect()");
487 exit(1);
488 }
489
490 /* Bundle cmd and argument into string, convert to UTF-8 if needed */
491 snprintf(data, CTRL_LINEBUF_SIZE, "%s %s", cmd, arg);
492 ret = utils_locale_to_utf8(data, strlen(data), tmp, CTRL_LINEBUF_SIZE - 1);
493
494 if (ret != 0)
495 goto bail_out;
496
497 /* escape the utf-8 string */
498 escaped = utils_string_escape(tmp);
499 if ((strlen(escaped) + 1) > CTRL_LINEBUF_SIZE - 1)
500 goto bail_out;
501
502 /* send escaped utf-8 command to master */
503 send(s, escaped, strlen(escaped), 0);
504 send(s, "\n", 1, 0);
505
506 /* read result from master */
507 fp = fdopen(s, "r");
508 index = 0;
509 while ((c = fgetc(fp)) != EOF && index < CTRL_RESULT_SIZE && c != '\n')
510 {
511 result[index] = c;
512 index++;
513 }
514 result[index - 1] = '\0';
515
516 if (strncmp(result, "ERROR ", 6) == 0)
517 {
518 if (sscanf(result, "ERROR %d", &ret) != 1)
519 ret = -1;
520 }
521
522 bail_out:
523 xfree(escaped);
524 shutdown(s, SHUT_RDWR);
525 close(s);
526
527 return ret;
528}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette