VirtualBox

source: vbox/trunk/src/VBox/RDP/client-1.8.3/rdesktop.c@ 74414

Last change on this file since 74414 was 65822, checked in by vboxsync, 8 years ago

rdesktop: build fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 44.2 KB
Line 
1/* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Entrypoint and utility functions
4 Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
5 Copyright 2002-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
6 Copyright 2010-2014 Henrik Andersson <hean01@cendio.se> for Cendio AB
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/*
23 * Oracle GPL Disclaimer: For the avoidance of doubt, except that if any license choice
24 * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
25 * the General Public License version 2 (GPLv2) at this time for any software where
26 * a choice of GPL license versions is made available with the language indicating
27 * that GPLv2 or any later version may be used, or where a choice of which version
28 * of the GPL is applied is otherwise unspecified.
29 */
30
31#include <stdarg.h> /* va_list va_start va_end */
32#include <unistd.h> /* read close getuid getgid getpid getppid gethostname */
33#include <fcntl.h> /* open */
34#include <pwd.h> /* getpwuid */
35#include <termios.h> /* tcgetattr tcsetattr */
36#include <sys/stat.h> /* stat */
37#include <sys/time.h> /* gettimeofday */
38#include <sys/times.h> /* times */
39#include <ctype.h> /* toupper */
40#include <limits.h>
41#include <errno.h>
42#include <signal.h>
43#include "rdesktop.h"
44
45#ifdef VBOX
46# include <VBox/version.h>
47# include <iprt/log.h>
48#endif
49
50#ifdef HAVE_LOCALE_H
51#include <locale.h>
52#endif
53#ifdef HAVE_ICONV
54#ifdef HAVE_LANGINFO_H
55#include <langinfo.h>
56#endif
57#endif
58
59#ifdef EGD_SOCKET
60#include <sys/types.h>
61#include <sys/socket.h> /* socket connect */
62#include <sys/un.h> /* sockaddr_un */
63#endif
64
65#include "ssl.h"
66#if defined(VBOX) && defined(OPENSSL_MANGLER)
67# include <iprt/initterm.h>
68#endif
69
70/* Reconnect timeout based on approxiamted cookie life-time */
71#define RECONNECT_TIMEOUT (3600+600)
72#define RDESKTOP_LICENSE_STORE "/.local/share/rdesktop/licenses"
73
74uint8 g_static_rdesktop_salt_16[16] = {
75 0xb8, 0x82, 0x29, 0x31, 0xc5, 0x39, 0xd9, 0x44,
76 0x54, 0x15, 0x5e, 0x14, 0x71, 0x38, 0xd5, 0x4d
77};
78
79char g_title[64] = "";
80char *g_username;
81char g_password[64] = "";
82char g_hostname[16] = "";
83char g_keymapname[PATH_MAX] = "";
84unsigned int g_keylayout = 0x409; /* Defaults to US keyboard layout */
85int g_keyboard_type = 0x4; /* Defaults to US keyboard layout */
86int g_keyboard_subtype = 0x0; /* Defaults to US keyboard layout */
87int g_keyboard_functionkeys = 0xc; /* Defaults to US keyboard layout */
88int g_sizeopt = 0; /* If non-zero, a special size has been
89 requested. If 1, the geometry will be fetched
90 from _NET_WORKAREA. If negative, absolute value
91 specifies the percent of the whole screen. */
92int g_width = 800;
93int g_height = 600;
94int g_xpos = 0;
95int g_ypos = 0;
96int g_pos = 0; /* 0 position unspecified,
97 1 specified,
98 2 xpos neg,
99 4 ypos neg */
100extern int g_tcp_port_rdp;
101int g_server_depth = -1;
102int g_win_button_size = 0; /* If zero, disable single app mode */
103RD_BOOL g_network_error = False;
104RD_BOOL g_bitmap_compression = True;
105RD_BOOL g_sendmotion = True;
106RD_BOOL g_bitmap_cache = True;
107RD_BOOL g_bitmap_cache_persist_enable = False;
108RD_BOOL g_bitmap_cache_precache = True;
109RD_BOOL g_use_ctrl = True;
110RD_BOOL g_encryption = True;
111RD_BOOL g_encryption_initial = True;
112RD_BOOL g_packet_encryption = True;
113RD_BOOL g_desktop_save = True; /* desktop save order */
114RD_BOOL g_polygon_ellipse_orders = True; /* polygon / ellipse orders */
115RD_BOOL g_fullscreen = False;
116RD_BOOL g_grab_keyboard = True;
117RD_BOOL g_hide_decorations = False;
118RDP_VERSION g_rdp_version = RDP_V5; /* Default to version 5 */
119RD_BOOL g_rdpclip = True;
120RD_BOOL g_console_session = False;
121#ifndef VBOX
122RD_BOOL g_numlock_sync = False;
123#else /* VBOX */
124/* Always use numlock synchronization with VRDP. */
125RD_BOOL g_numlock_sync = True;
126#endif /* VBOX */
127RD_BOOL g_lspci_enabled = False;
128RD_BOOL g_owncolmap = False;
129RD_BOOL g_ownbackstore = True; /* We can't rely on external BackingStore */
130RD_BOOL g_seamless_rdp = False;
131RD_BOOL g_use_password_as_pin = False;
132char g_seamless_shell[512];
133char g_seamless_spawn_cmd[512];
134RD_BOOL g_seamless_persistent_mode = True;
135RD_BOOL g_user_quit = False;
136uint32 g_embed_wnd;
137uint32 g_rdp5_performanceflags =
138 RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS | RDP5_NO_CURSOR_SHADOW;
139/* Session Directory redirection */
140RD_BOOL g_redirect = False;
141char *g_redirect_server;
142uint32 g_redirect_server_len;
143char *g_redirect_domain;
144uint32 g_redirect_domain_len;
145char *g_redirect_username;
146uint32 g_redirect_username_len;
147uint8 *g_redirect_lb_info;
148uint32 g_redirect_lb_info_len;
149uint8 *g_redirect_cookie;
150uint32 g_redirect_cookie_len;
151uint32 g_redirect_flags = 0;
152uint32 g_redirect_session_id = 0;
153
154uint32 g_reconnect_logonid = 0;
155char g_reconnect_random[16];
156time_t g_reconnect_random_ts;
157RD_BOOL g_has_reconnect_random = False;
158RD_BOOL g_reconnect_loop = False;
159uint8 g_client_random[SEC_RANDOM_SIZE];
160RD_BOOL g_pending_resize = False;
161
162#ifdef WITH_RDPSND
163RD_BOOL g_rdpsnd = False;
164#endif
165
166#ifdef WITH_RDPUSB
167RD_BOOL g_rdpusb = False;
168#endif
169
170#ifdef WITH_BIRD_VD_HACKS
171RD_BOOL g_keep_virtual_desktop_shortcuts = False;
172#endif
173
174#ifdef HAVE_ICONV
175char g_codepage[16] = "";
176#endif
177
178char *g_sc_csp_name = NULL; /* Smartcard CSP name */
179char *g_sc_reader_name = NULL;
180char *g_sc_card_name = NULL;
181char *g_sc_container_name = NULL;
182
183extern RDPDR_DEVICE g_rdpdr_device[];
184extern uint32 g_num_devices;
185extern char *g_rdpdr_clientname;
186
187#ifdef RDP2VNC
188extern int rfb_port;
189extern int defer_time;
190void
191rdp2vnc_connect(char *server, uint32 flags, char *domain, char *password,
192 char *shell, char *directory);
193#endif
194/* Display usage information */
195static void
196usage(char *program)
197{
198 fprintf(stderr, "rdesktop: A Remote Desktop Protocol client.\n");
199 fprintf(stderr,
200 "Version " PACKAGE_VERSION ". Copyright (C) 1999-2011 Matthew Chapman et al.\n");
201#ifdef VBOX
202 fprintf(stderr, "Modified for VirtualBox by " VBOX_VENDOR "\n");
203#endif
204 fprintf(stderr, "See http://www.rdesktop.org/ for more information.\n\n");
205
206 fprintf(stderr, "Usage: %s [options] server[:port]\n", program);
207#ifdef RDP2VNC
208 fprintf(stderr, " -V: vnc port\n");
209 fprintf(stderr, " -Q: defer time (ms)\n");
210#endif
211 fprintf(stderr, " -u: user name\n");
212 fprintf(stderr, " -d: domain\n");
213 fprintf(stderr, " -s: shell / seamless application to start remotly\n");
214 fprintf(stderr, " -c: working directory\n");
215 fprintf(stderr, " -p: password (- to prompt)\n");
216 fprintf(stderr, " -n: client hostname\n");
217 fprintf(stderr, " -k: keyboard layout on server (en-us, de, sv, etc.)\n");
218 fprintf(stderr, " -g: desktop geometry (WxH)\n");
219#ifdef WITH_SCARD
220 fprintf(stderr, " -i: enables smartcard authentication, password is used as pin\n");
221#endif
222 fprintf(stderr, " -f: full-screen mode\n");
223 fprintf(stderr, " -b: force bitmap updates\n");
224#ifdef HAVE_ICONV
225 fprintf(stderr, " -L: local codepage\n");
226#endif
227 fprintf(stderr, " -A: path to SeamlessRDP shell, this enables SeamlessRDP mode\n");
228 fprintf(stderr, " -B: use BackingStore of X-server (if available)\n");
229 fprintf(stderr, " -e: disable encryption (French TS)\n");
230 fprintf(stderr, " -E: disable encryption from client to server\n");
231 fprintf(stderr, " -m: do not send motion events\n");
232 fprintf(stderr, " -C: use private colour map\n");
233 fprintf(stderr, " -D: hide window manager decorations\n");
234 fprintf(stderr, " -K: keep window manager key bindings\n");
235 fprintf(stderr, " -S: caption button size (single application mode)\n");
236 fprintf(stderr, " -T: window title\n");
237 fprintf(stderr, " -t: disable use of remote ctrl\n");
238 fprintf(stderr, " -N: enable numlock syncronization\n");
239 fprintf(stderr, " -X: embed into another window with a given id.\n");
240 fprintf(stderr, " -a: connection colour depth\n");
241 fprintf(stderr, " -z: enable rdp compression\n");
242 fprintf(stderr, " -x: RDP5 experience (m[odem 28.8], b[roadband], l[an] or hex nr.)\n");
243 fprintf(stderr, " -P: use persistent bitmap caching\n");
244 fprintf(stderr, " -r: enable specified device redirection (this flag can be repeated)\n");
245 fprintf(stderr,
246 " '-r comport:COM1=/dev/ttyS0': enable serial redirection of /dev/ttyS0 to COM1\n");
247 fprintf(stderr, " or COM1=/dev/ttyS0,COM2=/dev/ttyS1\n");
248 fprintf(stderr,
249 " '-r disk:floppy=/mnt/floppy': enable redirection of /mnt/floppy to 'floppy' share\n");
250 fprintf(stderr, " or 'floppy=/mnt/floppy,cdrom=/mnt/cdrom'\n");
251 fprintf(stderr, " '-r clientname=<client name>': Set the client name displayed\n");
252 fprintf(stderr, " for redirected disks\n");
253 fprintf(stderr,
254 " '-r lptport:LPT1=/dev/lp0': enable parallel redirection of /dev/lp0 to LPT1\n");
255 fprintf(stderr, " or LPT1=/dev/lp0,LPT2=/dev/lp1\n");
256 fprintf(stderr, " '-r printer:mydeskjet': enable printer redirection\n");
257 fprintf(stderr,
258 " or mydeskjet=\"HP LaserJet IIIP\" to enter server driver as well\n");
259#ifdef WITH_RDPSND
260 fprintf(stderr,
261 " '-r sound:[local[:driver[:device]]|off|remote]': enable sound redirection\n");
262 fprintf(stderr, " remote would leave sound on server\n");
263 fprintf(stderr, " available drivers for 'local':\n");
264 rdpsnd_show_help();
265#endif
266#ifdef WITH_RDPUSB
267 fprintf(stderr,
268 " '-r usb': enable USB redirection\n");
269#endif
270 fprintf(stderr,
271 " '-r clipboard:[off|PRIMARYCLIPBOARD|CLIPBOARD]': enable clipboard\n");
272 fprintf(stderr, " redirection.\n");
273 fprintf(stderr,
274 " 'PRIMARYCLIPBOARD' looks at both PRIMARY and CLIPBOARD\n");
275 fprintf(stderr, " when sending data to server.\n");
276 fprintf(stderr, " 'CLIPBOARD' looks at only CLIPBOARD.\n");
277#ifdef WITH_SCARD
278 fprintf(stderr, " '-r scard[:\"Scard Name\"=\"Alias Name[;Vendor Name]\"[,...]]\n");
279 fprintf(stderr, " example: -r scard:\"eToken PRO 00 00\"=\"AKS ifdh 0\"\n");
280 fprintf(stderr,
281 " \"eToken PRO 00 00\" -> Device in Linux/Unix enviroment\n");
282 fprintf(stderr,
283 " \"AKS ifdh 0\" -> Device shown in Windows enviroment \n");
284 fprintf(stderr, " example: -r scard:\"eToken PRO 00 00\"=\"AKS ifdh 0;AKS\"\n");
285 fprintf(stderr,
286 " \"eToken PRO 00 00\" -> Device in Linux/Unix enviroment\n");
287 fprintf(stderr,
288 " \"AKS ifdh 0\" -> Device shown in Windows enviroment \n");
289 fprintf(stderr,
290 " \"AKS\" -> Device vendor name \n");
291#endif
292 fprintf(stderr, " -0: attach to console\n");
293 fprintf(stderr, " -4: use RDP version 4\n");
294 fprintf(stderr, " -5: use RDP version 5 (default)\n");
295#ifdef WITH_BIRD_VD_HACKS
296 fprintf(stderr, " -H keep-virtual-desktop-shortcuts: Keep keyboard shortcuts typical\n"
297 " for switching virtual desktops (C-A-Left/Right). \n");
298#endif
299#ifdef WITH_SCARD
300 fprintf(stderr, " -o: name=value: Adds an additional option to rdesktop.\n");
301 fprintf(stderr,
302 " sc-csp-name Specifies the Crypto Service Provider name which\n");
303 fprintf(stderr,
304 " is used to authenticate the user by smartcard\n");
305 fprintf(stderr,
306 " sc-container-name Specifies the container name, this is usally the username\n");
307 fprintf(stderr, " sc-reader-name Smartcard reader name to use\n");
308 fprintf(stderr,
309 " sc-card-name Specifies the card name of the smartcard to use\n");
310#endif
311
312 fprintf(stderr, "\n");
313
314}
315
316static int
317handle_disconnect_reason(RD_BOOL deactivated, uint16 reason)
318{
319 char *text;
320 int retval;
321
322 switch (reason)
323 {
324 case exDiscReasonNoInfo:
325 text = "No information available";
326 if (deactivated)
327 retval = EX_OK;
328 else
329 retval = EXRD_UNKNOWN;
330 break;
331
332 case exDiscReasonAPIInitiatedDisconnect:
333 text = "Server initiated disconnect";
334 retval = EXRD_API_DISCONNECT;
335 break;
336
337 case exDiscReasonAPIInitiatedLogoff:
338 text = "Server initiated logoff";
339 retval = EXRD_API_LOGOFF;
340 break;
341
342 case exDiscReasonServerIdleTimeout:
343 text = "Server idle timeout reached";
344 retval = EXRD_IDLE_TIMEOUT;
345 break;
346
347 case exDiscReasonServerLogonTimeout:
348 text = "Server logon timeout reached";
349 retval = EXRD_LOGON_TIMEOUT;
350 break;
351
352 case exDiscReasonReplacedByOtherConnection:
353 text = "The session was replaced";
354 retval = EXRD_REPLACED;
355 break;
356
357 case exDiscReasonOutOfMemory:
358 text = "The server is out of memory";
359 retval = EXRD_OUT_OF_MEM;
360 break;
361
362 case exDiscReasonServerDeniedConnection:
363 text = "The server denied the connection";
364 retval = EXRD_DENIED;
365 break;
366
367 case exDiscReasonServerDeniedConnectionFips:
368 text = "The server denied the connection for security reason";
369 retval = EXRD_DENIED_FIPS;
370 break;
371
372 case exDiscReasonServerInsufficientPrivileges:
373 text = "The user cannot connect to the server due to insufficient access privileges.";
374 retval = EXRD_INSUFFICIENT_PRIVILEGES;
375 break;
376
377 case exDiscReasonServerFreshCredentialsRequired:
378 text = "The server does not accept saved user credentials and requires that the user enter their credentials for each connection.";
379 retval = EXRD_FRESH_CREDENTIALS_REQUIRED;
380 break;
381
382 case exDiscReasonRPCInitiatedDisconnectByUser:
383 text = "Disconnect initiated by administration tool";
384 retval = EXRD_RPC_DISCONNECT_BY_USER;
385 break;
386
387 case exDiscReasonByUser:
388 text = "Disconnect initiated by user";
389 retval = EXRD_DISCONNECT_BY_USER;
390 break;
391
392 case exDiscReasonLicenseInternal:
393 text = "Internal licensing error";
394 retval = EXRD_LIC_INTERNAL;
395 break;
396
397 case exDiscReasonLicenseNoLicenseServer:
398 text = "No license server available";
399 retval = EXRD_LIC_NOSERVER;
400 break;
401
402 case exDiscReasonLicenseNoLicense:
403 text = "No valid license available";
404 retval = EXRD_LIC_NOLICENSE;
405 break;
406
407 case exDiscReasonLicenseErrClientMsg:
408 text = "Invalid licensing message";
409 retval = EXRD_LIC_MSG;
410 break;
411
412 case exDiscReasonLicenseHwidDoesntMatchLicense:
413 text = "Hardware id doesn't match software license";
414 retval = EXRD_LIC_HWID;
415 break;
416
417 case exDiscReasonLicenseErrClientLicense:
418 text = "Client license error";
419 retval = EXRD_LIC_CLIENT;
420 break;
421
422 case exDiscReasonLicenseCantFinishProtocol:
423 text = "Network error during licensing protocol";
424 retval = EXRD_LIC_NET;
425 break;
426
427 case exDiscReasonLicenseClientEndedProtocol:
428 text = "Licensing protocol was not completed";
429 retval = EXRD_LIC_PROTO;
430 break;
431
432 case exDiscReasonLicenseErrClientEncryption:
433 text = "Incorrect client license encryption";
434 retval = EXRD_LIC_ENC;
435 break;
436
437 case exDiscReasonLicenseCantUpgradeLicense:
438 text = "Can't upgrade license";
439 retval = EXRD_LIC_UPGRADE;
440 break;
441
442 case exDiscReasonLicenseNoRemoteConnections:
443 text = "The server is not licensed to accept remote connections";
444 retval = EXRD_LIC_NOREMOTE;
445 break;
446
447 default:
448 if (reason > 0x1000 && reason < 0x7fff)
449 {
450 text = "Internal protocol error";
451 }
452 else
453 {
454 text = "Unknown reason";
455 }
456 retval = EXRD_UNKNOWN;
457 }
458 if (reason != exDiscReasonNoInfo)
459 fprintf(stderr, "disconnect: %s.\n", text);
460
461 return retval;
462}
463
464static void
465rdesktop_reset_state(void)
466{
467 rdp_reset_state();
468#ifdef WITH_SCARD
469 scard_reset_state();
470#endif
471#ifdef WITH_RDPSND
472 rdpsnd_reset_state();
473#endif
474}
475
476static RD_BOOL
477read_password(char *password, int size)
478{
479 struct termios tios;
480 RD_BOOL ret = False;
481 int istty = 0;
482 char *p;
483
484 if (tcgetattr(STDIN_FILENO, &tios) == 0)
485 {
486 fprintf(stderr, "Password: ");
487 tios.c_lflag &= ~ECHO;
488 tcsetattr(STDIN_FILENO, TCSANOW, &tios);
489 istty = 1;
490 }
491
492 if (fgets(password, size, stdin) != NULL)
493 {
494 ret = True;
495
496 /* strip final newline */
497 p = strchr(password, '\n');
498 if (p != NULL)
499 *p = 0;
500 }
501
502 if (istty)
503 {
504 tios.c_lflag |= ECHO;
505 tcsetattr(STDIN_FILENO, TCSANOW, &tios);
506 fprintf(stderr, "\n");
507 }
508
509 return ret;
510}
511
512static void
513parse_server_and_port(char *server)
514{
515 char *p;
516#ifdef IPv6
517 int addr_colons;
518#endif
519
520#ifdef IPv6
521 p = server;
522 addr_colons = 0;
523 while (*p)
524 if (*p++ == ':')
525 addr_colons++;
526 if (addr_colons >= 2)
527 {
528 /* numeric IPv6 style address format - [1:2:3::4]:port */
529 p = strchr(server, ']');
530 if (*server == '[' && p != NULL)
531 {
532 if (*(p + 1) == ':' && *(p + 2) != '\0')
533 g_tcp_port_rdp = strtol(p + 2, NULL, 10);
534 /* remove the port number and brackets from the address */
535 *p = '\0';
536 strncpy(server, server + 1, strlen(server));
537 }
538 }
539 else
540 {
541 /* dns name or IPv4 style address format - server.example.com:port or 1.2.3.4:port */
542 p = strchr(server, ':');
543 if (p != NULL)
544 {
545 g_tcp_port_rdp = strtol(p + 1, NULL, 10);
546 *p = 0;
547 }
548 }
549#else /* no IPv6 support */
550 p = strchr(server, ':');
551 if (p != NULL)
552 {
553 g_tcp_port_rdp = strtol(p + 1, NULL, 10);
554 *p = 0;
555 }
556#endif /* IPv6 */
557
558}
559
560#ifdef VBOX
561/* This disables iprt logging */
562DECLEXPORT(PRTLOGGER) RTCALL RTLogDefaultInit(void)
563{
564 return NULL;
565}
566#endif
567
568/* Client program */
569int
570main(int argc, char *argv[])
571{
572 char server[256];
573 char fullhostname[64];
574 char domain[256];
575 char shell[256];
576 char directory[256];
577 RD_BOOL prompt_password, deactivated;
578 struct passwd *pw;
579 uint32 flags, ext_disc_reason = 0;
580 char *p;
581 int c;
582 char *locale = NULL;
583 int username_option = 0;
584 RD_BOOL geometry_option = False;
585#ifdef WITH_RDPSND
586 char *rdpsnd_optarg = NULL;
587#endif
588
589#if defined(VBOX) && defined(OPENSSL_MANGLER)
590 /* Only need RT initialization if building against OpenSSL using
591 * RT synchronization, standalone rdesktop doesn't need this. */
592 RTR3InitExe(argc, &argv, 0);
593#endif
594
595#ifdef HAVE_LOCALE_H
596 /* Set locale according to environment */
597 locale = setlocale(LC_ALL, "");
598 if (locale)
599 {
600 locale = xstrdup(locale);
601 }
602
603#endif
604
605 /* Ignore SIGPIPE, since we are using popen() */
606 struct sigaction act;
607 memset(&act, 0, sizeof(act));
608 act.sa_handler = SIG_IGN;
609 sigemptyset(&act.sa_mask);
610 act.sa_flags = 0;
611 sigaction(SIGPIPE, &act, NULL);
612
613 /* setup default flags for TS_INFO_PACKET */
614 flags = RDP_INFO_MOUSE | RDP_INFO_DISABLECTRLALTDEL
615 | RDP_INFO_UNICODE | RDP_INFO_MAXIMIZESHELL | RDP_INFO_ENABLEWINDOWSKEY;
616
617 prompt_password = False;
618 g_seamless_spawn_cmd[0] = domain[0] = g_password[0] = shell[0] = directory[0] = 0;
619 g_embed_wnd = 0;
620
621 g_num_devices = 0;
622
623#ifdef RDP2VNC
624#define VNCOPT "V:Q:"
625#else
626#define VNCOPT
627#endif
628#ifdef WITH_BIRD_VD_HACKS
629#define VDHOPT "H:"
630#else
631#define VDHOPT
632#endif
633
634 while ((c = getopt(argc, argv,
635 VNCOPT VDHOPT "A:u:L:d:s:c:p:n:k:g:o:fbBeEitmzCDKS:T:NX:a:x:Pr:045h?")) != -1)
636 {
637 switch (c)
638 {
639#ifdef RDP2VNC
640 case 'V':
641 rfb_port = strtol(optarg, NULL, 10);
642 if (rfb_port < 100)
643 rfb_port += 5900;
644 break;
645
646 case 'Q':
647 defer_time = strtol(optarg, NULL, 10);
648 if (defer_time < 0)
649 defer_time = 0;
650 break;
651#endif
652
653 case 'A':
654 g_seamless_rdp = True;
655 STRNCPY(g_seamless_shell, optarg, sizeof(g_seamless_shell));
656 break;
657
658 case 'u':
659 g_username = (char *) xmalloc(strlen(optarg) + 1);
660 STRNCPY(g_username, optarg, strlen(optarg) + 1);
661 username_option = 1;
662 break;
663
664 case 'L':
665#ifdef HAVE_ICONV
666 STRNCPY(g_codepage, optarg, sizeof(g_codepage));
667#else
668 error("iconv support not available\n");
669#endif
670 break;
671
672 case 'd':
673 STRNCPY(domain, optarg, sizeof(domain));
674 break;
675
676 case 's':
677 STRNCPY(shell, optarg, sizeof(shell));
678 g_seamless_persistent_mode = False;
679 break;
680
681 case 'c':
682 STRNCPY(directory, optarg, sizeof(directory));
683 break;
684
685 case 'p':
686 if ((optarg[0] == '-') && (optarg[1] == 0))
687 {
688 prompt_password = True;
689 break;
690 }
691
692 STRNCPY(g_password, optarg, sizeof(g_password));
693 flags |= RDP_INFO_AUTOLOGON;
694
695 /* try to overwrite argument so it won't appear in ps */
696 p = optarg;
697 while (*p)
698 *(p++) = 'X';
699 break;
700#ifdef WITH_SCARD
701 case 'i':
702 flags |= RDP_INFO_PASSWORD_IS_SC_PIN;
703 g_use_password_as_pin = True;
704 break;
705#endif
706 case 't':
707 g_use_ctrl = False;
708 break;
709
710 case 'n':
711 STRNCPY(g_hostname, optarg, sizeof(g_hostname));
712 break;
713
714 case 'k':
715 STRNCPY(g_keymapname, optarg, sizeof(g_keymapname));
716 break;
717
718 case 'g':
719 geometry_option = True;
720 g_fullscreen = False;
721 if (!strcmp(optarg, "workarea"))
722 {
723 g_sizeopt = 1;
724 break;
725 }
726
727 g_width = strtol(optarg, &p, 10);
728 if (g_width <= 0)
729 {
730 error("invalid geometry\n");
731 return EX_USAGE;
732 }
733
734 if (*p == 'x')
735 g_height = strtol(p + 1, &p, 10);
736
737 if (g_height <= 0)
738 {
739 error("invalid geometry\n");
740 return EX_USAGE;
741 }
742
743 if (*p == '%')
744 {
745 g_sizeopt = -g_width;
746 g_width = 800;
747 p++;
748 }
749
750 if (*p == '+' || *p == '-')
751 {
752 g_pos |= (*p == '-') ? 2 : 1;
753 g_xpos = strtol(p, &p, 10);
754
755 }
756 if (*p == '+' || *p == '-')
757 {
758 g_pos |= (*p == '-') ? 4 : 1;
759 g_ypos = strtol(p, NULL, 10);
760 }
761
762 break;
763
764 case 'f':
765 g_fullscreen = True;
766 break;
767
768 case 'b':
769 g_bitmap_cache = False;
770 break;
771
772 case 'B':
773 g_ownbackstore = False;
774 break;
775
776 case 'e':
777 g_encryption_initial = g_encryption = False;
778 break;
779 case 'E':
780 g_packet_encryption = False;
781 break;
782 case 'm':
783 g_sendmotion = False;
784 break;
785
786 case 'C':
787 g_owncolmap = True;
788 break;
789
790 case 'D':
791 g_hide_decorations = True;
792 break;
793
794 case 'K':
795 g_grab_keyboard = False;
796 break;
797
798 case 'S':
799 if (!strcmp(optarg, "standard"))
800 {
801 g_win_button_size = 18;
802 break;
803 }
804
805 g_win_button_size = strtol(optarg, &p, 10);
806
807 if (*p)
808 {
809 error("invalid button size\n");
810 return EX_USAGE;
811 }
812
813 break;
814
815 case 'T':
816 STRNCPY(g_title, optarg, sizeof(g_title));
817 break;
818
819 case 'N':
820 g_numlock_sync = True;
821 break;
822
823 case 'X':
824 g_embed_wnd = strtol(optarg, NULL, 0);
825 break;
826
827 case 'a':
828 g_server_depth = strtol(optarg, NULL, 10);
829 if (g_server_depth != 8 &&
830 g_server_depth != 16 &&
831 g_server_depth != 15 && g_server_depth != 24
832 && g_server_depth != 32)
833 {
834 error("Invalid server colour depth.\n");
835 return EX_USAGE;
836 }
837 break;
838
839 case 'z':
840 DEBUG(("rdp compression enabled\n"));
841 flags |= (RDP_INFO_COMPRESSION | RDP_INFO_COMPRESSION2);
842 break;
843
844 case 'x':
845 if (str_startswith(optarg, "m")) /* modem */
846 {
847 g_rdp5_performanceflags = RDP5_NO_CURSOR_SHADOW |
848 RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG |
849 RDP5_NO_MENUANIMATIONS | RDP5_NO_THEMING;
850 }
851 else if (str_startswith(optarg, "b")) /* broadband */
852 {
853 g_rdp5_performanceflags =
854 RDP5_NO_CURSOR_SHADOW | RDP5_NO_WALLPAPER;
855 }
856 else if (str_startswith(optarg, "l")) /* lan */
857 {
858 g_rdp5_performanceflags =
859 RDP5_NO_CURSOR_SHADOW | RDP5_DISABLE_NOTHING;
860 }
861 else
862 {
863 g_rdp5_performanceflags =
864 RDP5_NO_CURSOR_SHADOW | strtol(optarg, NULL, 16);
865 }
866 break;
867
868 case 'P':
869 g_bitmap_cache_persist_enable = True;
870 break;
871
872 case 'r':
873
874 if (str_startswith(optarg, "sound"))
875 {
876 optarg += 5;
877
878 if (*optarg == ':')
879 {
880 optarg++;
881 while ((p = next_arg(optarg, ',')))
882 {
883 if (str_startswith(optarg, "remote"))
884 flags |= RDP_INFO_REMOTE_CONSOLE_AUDIO;
885
886 if (str_startswith(optarg, "local"))
887#ifdef WITH_RDPSND
888 {
889 rdpsnd_optarg =
890 next_arg(optarg, ':');
891 g_rdpsnd = True;
892 }
893
894#else
895 warning("Not compiled with sound support\n");
896#endif
897
898 if (str_startswith(optarg, "off"))
899#ifdef WITH_RDPSND
900 g_rdpsnd = False;
901#else
902 warning("Not compiled with sound support\n");
903#endif
904
905 optarg = p;
906 }
907 }
908 else
909 {
910#ifdef WITH_RDPSND
911 g_rdpsnd = True;
912#else
913 warning("Not compiled with sound support\n");
914#endif
915 }
916 }
917 else if (str_startswith(optarg, "usb"))
918 {
919#ifdef WITH_RDPUSB
920 g_rdpusb = True;
921#else
922 warning("Not compiled with USB support\n");
923#endif
924 }
925 else if (str_startswith(optarg, "disk"))
926 {
927 /* -r disk:h:=/mnt/floppy */
928 disk_enum_devices(&g_num_devices, optarg + 4);
929 }
930 else if (str_startswith(optarg, "comport"))
931 {
932 serial_enum_devices(&g_num_devices, optarg + 7);
933 }
934 else if (str_startswith(optarg, "lspci"))
935 {
936 g_lspci_enabled = True;
937 }
938 else if (str_startswith(optarg, "lptport"))
939 {
940 parallel_enum_devices(&g_num_devices, optarg + 7);
941 }
942 else if (str_startswith(optarg, "printer"))
943 {
944 printer_enum_devices(&g_num_devices, optarg + 7);
945 }
946 else if (str_startswith(optarg, "clientname"))
947 {
948 g_rdpdr_clientname = xmalloc(strlen(optarg + 11) + 1);
949 strcpy(g_rdpdr_clientname, optarg + 11);
950 }
951 else if (str_startswith(optarg, "clipboard"))
952 {
953 optarg += 9;
954
955 if (*optarg == ':')
956 {
957 optarg++;
958
959 if (str_startswith(optarg, "off"))
960 g_rdpclip = False;
961 else
962 cliprdr_set_mode(optarg);
963 }
964 else
965 g_rdpclip = True;
966 }
967 else if (strncmp("scard", optarg, 5) == 0)
968 {
969#ifdef WITH_SCARD
970 scard_enum_devices(&g_num_devices, optarg + 5);
971#else
972 warning("Not compiled with smartcard support\n");
973#endif
974 }
975 else
976 {
977 warning("Unknown -r argument\n\n\tPossible arguments are: comport, disk, lptport, printer, sound, clipboard, scard\n");
978 }
979 break;
980
981 case '0':
982 g_console_session = True;
983 break;
984
985 case '4':
986 g_rdp_version = RDP_V4;
987 break;
988
989 case '5':
990 g_rdp_version = RDP_V5;
991 break;
992#if WITH_SCARD
993 case 'o':
994 {
995 char *p = strchr(optarg, '=');
996 if (p == NULL)
997 {
998 warning("Skipping option '%s' specified, lacks name=value format.\n");
999 continue;
1000 }
1001
1002 if (strncmp(optarg, "sc-csp-name", strlen("sc-scp-name")) ==
1003 0)
1004 g_sc_csp_name = strdup(p + 1);
1005 else if (strncmp
1006 (optarg, "sc-reader-name",
1007 strlen("sc-reader-name")) == 0)
1008 g_sc_reader_name = strdup(p + 1);
1009 else if (strncmp
1010 (optarg, "sc-card-name",
1011 strlen("sc-card-name")) == 0)
1012 g_sc_card_name = strdup(p + 1);
1013 else if (strncmp
1014 (optarg, "sc-container-name",
1015 strlen("sc-container-name")) == 0)
1016 g_sc_container_name = strdup(p + 1);
1017
1018 }
1019 break;
1020#endif
1021
1022#ifdef WITH_BIRD_VD_HACKS
1023 case 'H': /* hacks */
1024 if (!strcmp(optarg, "keep-virtual-desktop-shortcuts"))
1025 g_keep_virtual_desktop_shortcuts = True;
1026 else
1027 error("Unknown -H argument\n\n\tPossible argument is: keep-virtual-desktop-shortcuts\n");
1028 break;
1029#endif
1030
1031 case 'h':
1032 case '?':
1033 default:
1034 usage(argv[0]);
1035 return EX_USAGE;
1036 }
1037 }
1038
1039 if (argc - optind != 1)
1040 {
1041 usage(argv[0]);
1042 return EX_USAGE;
1043 }
1044
1045 STRNCPY(server, argv[optind], sizeof(server));
1046 parse_server_and_port(server);
1047
1048 if (g_seamless_rdp)
1049 {
1050 if (shell[0])
1051 STRNCPY(g_seamless_spawn_cmd, shell, sizeof(g_seamless_spawn_cmd));
1052
1053 STRNCPY(shell, g_seamless_shell, sizeof(shell));
1054
1055 if (g_win_button_size)
1056 {
1057 error("You cannot use -S and -A at the same time\n");
1058 return EX_USAGE;
1059 }
1060 g_rdp5_performanceflags &= ~RDP5_NO_FULLWINDOWDRAG;
1061 if (geometry_option)
1062 {
1063 error("You cannot use -g and -A at the same time\n");
1064 return EX_USAGE;
1065 }
1066 if (g_fullscreen)
1067 {
1068 error("You cannot use -f and -A at the same time\n");
1069 return EX_USAGE;
1070 }
1071 if (g_hide_decorations)
1072 {
1073 error("You cannot use -D and -A at the same time\n");
1074 return EX_USAGE;
1075 }
1076 if (g_embed_wnd)
1077 {
1078 error("You cannot use -X and -A at the same time\n");
1079 return EX_USAGE;
1080 }
1081 if (g_rdp_version < RDP_V5)
1082 {
1083 error("You cannot use -4 and -A at the same time\n");
1084 return EX_USAGE;
1085 }
1086 g_sizeopt = -100;
1087 g_grab_keyboard = False;
1088 }
1089
1090 if (!username_option)
1091 {
1092 pw = getpwuid(getuid());
1093 if ((pw == NULL) || (pw->pw_name == NULL))
1094 {
1095 error("could not determine username, use -u\n");
1096 return EX_OSERR;
1097 }
1098 /* +1 for trailing \0 */
1099 int pwlen = strlen(pw->pw_name) + 1;
1100 g_username = (char *) xmalloc(pwlen);
1101 STRNCPY(g_username, pw->pw_name, pwlen);
1102 }
1103
1104#ifdef HAVE_ICONV
1105 if (g_codepage[0] == 0)
1106 {
1107 if (setlocale(LC_CTYPE, ""))
1108 {
1109 STRNCPY(g_codepage, nl_langinfo(CODESET), sizeof(g_codepage));
1110 }
1111 else
1112 {
1113 STRNCPY(g_codepage, DEFAULT_CODEPAGE, sizeof(g_codepage));
1114 }
1115 }
1116#endif
1117
1118 if (g_hostname[0] == 0)
1119 {
1120 if (gethostname(fullhostname, sizeof(fullhostname)) == -1)
1121 {
1122 error("could not determine local hostname, use -n\n");
1123 return EX_OSERR;
1124 }
1125
1126 p = strchr(fullhostname, '.');
1127 if (p != NULL)
1128 *p = 0;
1129
1130 STRNCPY(g_hostname, fullhostname, sizeof(g_hostname));
1131 }
1132
1133 if (g_keymapname[0] == 0)
1134 {
1135 if (locale && xkeymap_from_locale(locale))
1136 {
1137 fprintf(stderr, "Autoselected keyboard map %s\n", g_keymapname);
1138 }
1139 else
1140 {
1141 STRNCPY(g_keymapname, "en-us", sizeof(g_keymapname));
1142 }
1143 }
1144 if (locale)
1145 xfree(locale);
1146
1147
1148 if (prompt_password && read_password(g_password, sizeof(g_password)))
1149 flags |= RDP_INFO_AUTOLOGON;
1150
1151 if (g_title[0] == 0)
1152 {
1153 strcpy(g_title, "rdesktop - ");
1154 strncat(g_title, server, sizeof(g_title) - sizeof("rdesktop - "));
1155 }
1156
1157#ifdef RDP2VNC
1158 rdp2vnc_connect(server, flags, domain, g_password, shell, directory);
1159 return EX_OK;
1160#else
1161
1162 /* Only startup ctrl functionality is seamless are used for now. */
1163 if (g_use_ctrl && g_seamless_rdp)
1164 {
1165 if (ctrl_init(server, domain, g_username) < 0)
1166 {
1167 error("Failed to initialize ctrl mode.");
1168 exit(1);
1169 }
1170
1171 if (ctrl_is_slave())
1172 {
1173 fprintf(stdout,
1174 "rdesktop in slave mode sending command to master process.\n");
1175
1176 if (g_seamless_spawn_cmd[0])
1177 return ctrl_send_command("seamless.spawn", g_seamless_spawn_cmd);
1178
1179 fprintf(stdout, "No command specified to be spawn in seamless mode.\n");
1180 return EX_USAGE;
1181 }
1182 }
1183
1184 if (!ui_init())
1185 return EX_OSERR;
1186
1187#ifdef WITH_RDPSND
1188 if (!rdpsnd_init(rdpsnd_optarg))
1189 warning("Initializing sound-support failed!\n");
1190#endif
1191
1192#ifdef WITH_RDPUSB
1193 if (g_rdpusb)
1194 rdpusb_init();
1195#endif
1196
1197 if (g_lspci_enabled)
1198 lspci_init();
1199
1200 rdpdr_init();
1201 g_reconnect_loop = False;
1202 while (1)
1203 {
1204 rdesktop_reset_state();
1205
1206 if (g_redirect)
1207 {
1208 STRNCPY(domain, g_redirect_domain, sizeof(domain));
1209 xfree(g_username);
1210 g_username = (char *) xmalloc(strlen(g_redirect_username) + 1);
1211 STRNCPY(g_username, g_redirect_username, strlen(g_redirect_username) + 1);
1212 STRNCPY(server, g_redirect_server, sizeof(server));
1213 flags |= RDP_INFO_AUTOLOGON;
1214
1215 fprintf(stderr, "Redirected to %s@%s session %d.\n",
1216 g_redirect_username, g_redirect_server, g_redirect_session_id);
1217
1218 /* A redirect on SSL from a 2003 WTS will result in a 'connection reset by peer'
1219 and therefor we just clear this error before we connect to redirected server.
1220 */
1221 g_network_error = False;
1222 g_redirect = False;
1223 }
1224
1225 ui_init_connection();
1226 if (!rdp_connect
1227 (server, flags, domain, g_password, shell, directory, g_reconnect_loop))
1228 {
1229
1230 g_network_error = False;
1231
1232 if (g_reconnect_loop == False)
1233 return EX_PROTOCOL;
1234
1235 /* check if auto reconnect cookie has timed out */
1236 if (time(NULL) - g_reconnect_random_ts > RECONNECT_TIMEOUT)
1237 {
1238 fprintf(stderr, "Tried to reconnect for %d minutes, giving up.\n",
1239 RECONNECT_TIMEOUT / 60);
1240 return EX_PROTOCOL;
1241 }
1242
1243 sleep(4);
1244 continue;
1245 }
1246
1247 if (g_redirect)
1248 {
1249 rdp_disconnect();
1250 continue;
1251 }
1252
1253 /* By setting encryption to False here, we have an encrypted login
1254 packet but unencrypted transfer of other packets */
1255 if (!g_packet_encryption)
1256 g_encryption_initial = g_encryption = False;
1257
1258 DEBUG(("Connection successful.\n"));
1259
1260 rd_create_ui();
1261 tcp_run_ui(True);
1262
1263 deactivated = False;
1264 g_reconnect_loop = False;
1265 rdp_main_loop(&deactivated, &ext_disc_reason);
1266
1267 tcp_run_ui(False);
1268
1269 DEBUG(("Disconnecting...\n"));
1270 rdp_disconnect();
1271
1272 if (g_redirect)
1273 continue;
1274
1275 /* handle network error and start autoreconnect */
1276 if (g_network_error && !deactivated)
1277 {
1278 fprintf(stderr,
1279 "Disconnected due to network error, retrying to reconnect for %d minutes.\n",
1280 RECONNECT_TIMEOUT / 60);
1281 g_network_error = False;
1282 g_reconnect_loop = True;
1283 continue;
1284 }
1285
1286 ui_seamless_end();
1287 ui_destroy_window();
1288
1289 /* Enter a reconnect loop if we have a pending resize request */
1290 if (g_pending_resize)
1291 {
1292 g_pending_resize = False;
1293 g_reconnect_loop = True;
1294 continue;
1295 }
1296 break;
1297 }
1298
1299 cache_save_state();
1300 ui_deinit();
1301
1302#ifdef WITH_RDPUSB
1303 if (g_rdpusb)
1304 rdpusb_close();
1305#endif
1306
1307 if (g_user_quit)
1308 return EXRD_WINDOW_CLOSED;
1309
1310 return handle_disconnect_reason(deactivated, ext_disc_reason);
1311
1312#endif
1313 if (g_redirect_username)
1314 xfree(g_redirect_username);
1315
1316 xfree(g_username);
1317}
1318
1319#ifdef EGD_SOCKET
1320/* Read 32 random bytes from PRNGD or EGD socket (based on OpenSSL RAND_egd) */
1321static RD_BOOL
1322generate_random_egd(uint8 * buf)
1323{
1324 struct sockaddr_un addr;
1325 RD_BOOL ret = False;
1326 int fd;
1327
1328 fd = socket(AF_UNIX, SOCK_STREAM, 0);
1329 if (fd == -1)
1330 return False;
1331
1332 addr.sun_family = AF_UNIX;
1333 memcpy(addr.sun_path, EGD_SOCKET, sizeof(EGD_SOCKET));
1334 if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
1335 goto err;
1336
1337 /* PRNGD and EGD use a simple communications protocol */
1338 buf[0] = 1; /* Non-blocking (similar to /dev/urandom) */
1339 buf[1] = 32; /* Number of requested random bytes */
1340 if (write(fd, buf, 2) != 2)
1341 goto err;
1342
1343 if ((read(fd, buf, 1) != 1) || (buf[0] == 0)) /* Available? */
1344 goto err;
1345
1346 if (read(fd, buf, 32) != 32)
1347 goto err;
1348
1349 ret = True;
1350
1351 err:
1352 close(fd);
1353 return ret;
1354}
1355#endif
1356
1357/* Generate a 32-byte random for the secure transport code. */
1358void
1359generate_random(uint8 * random)
1360{
1361 struct stat st;
1362 struct tms tmsbuf;
1363 RDSSL_MD5 md5;
1364 uint32 *r;
1365 int fd, n;
1366
1367 /* If we have a kernel random device, try that first */
1368 if (((fd = open("/dev/urandom", O_RDONLY)) != -1)
1369 || ((fd = open("/dev/random", O_RDONLY)) != -1))
1370 {
1371 n = read(fd, random, 32);
1372 close(fd);
1373 if (n == 32)
1374 return;
1375 }
1376
1377#ifdef EGD_SOCKET
1378 /* As a second preference use an EGD */
1379 if (generate_random_egd(random))
1380 return;
1381#endif
1382
1383 /* Otherwise use whatever entropy we can gather - ideas welcome. */
1384 r = (uint32 *) random;
1385 r[0] = (getpid()) | (getppid() << 16);
1386 r[1] = (getuid()) | (getgid() << 16);
1387 r[2] = times(&tmsbuf); /* system uptime (clocks) */
1388 gettimeofday((struct timeval *) &r[3], NULL); /* sec and usec */
1389 stat("/tmp", &st);
1390 r[5] = st.st_atime;
1391 r[6] = st.st_mtime;
1392 r[7] = st.st_ctime;
1393
1394 /* Hash both halves with MD5 to obscure possible patterns */
1395 rdssl_md5_init(&md5);
1396 rdssl_md5_update(&md5, random, 16);
1397 rdssl_md5_final(&md5, random);
1398 rdssl_md5_update(&md5, random + 16, 16);
1399 rdssl_md5_final(&md5, random + 16);
1400}
1401
1402/* malloc; exit if out of memory */
1403void *
1404xmalloc(int size)
1405{
1406 void *mem = malloc(size);
1407 if (mem == NULL)
1408 {
1409 error("xmalloc %d\n", size);
1410 exit(EX_UNAVAILABLE);
1411 }
1412 return mem;
1413}
1414
1415/* Exit on NULL pointer. Use to verify result from XGetImage etc */
1416void
1417exit_if_null(void *ptr)
1418{
1419 if (ptr == NULL)
1420 {
1421 error("unexpected null pointer. Out of memory?\n");
1422 exit(EX_UNAVAILABLE);
1423 }
1424}
1425
1426/* strdup */
1427char *
1428xstrdup(const char *s)
1429{
1430 char *mem = strdup(s);
1431 if (mem == NULL)
1432 {
1433 perror("strdup");
1434 exit(EX_UNAVAILABLE);
1435 }
1436 return mem;
1437}
1438
1439/* realloc; exit if out of memory */
1440void *
1441xrealloc(void *oldmem, size_t size)
1442{
1443 void *mem;
1444
1445 if (size == 0)
1446 size = 1;
1447 mem = realloc(oldmem, size);
1448 if (mem == NULL)
1449 {
1450 error("xrealloc %ld\n", size);
1451 exit(EX_UNAVAILABLE);
1452 }
1453 return mem;
1454}
1455
1456/* free */
1457void
1458xfree(void *mem)
1459{
1460 free(mem);
1461}
1462
1463/* report an error */
1464void
1465error(char *format, ...)
1466{
1467 va_list ap;
1468
1469 fprintf(stderr, "ERROR: ");
1470
1471 va_start(ap, format);
1472 vfprintf(stderr, format, ap);
1473 va_end(ap);
1474}
1475
1476/* report a warning */
1477void
1478warning(char *format, ...)
1479{
1480 va_list ap;
1481
1482 fprintf(stderr, "WARNING: ");
1483
1484 va_start(ap, format);
1485 vfprintf(stderr, format, ap);
1486 va_end(ap);
1487}
1488
1489/* report an unimplemented protocol feature */
1490void
1491unimpl(char *format, ...)
1492{
1493 va_list ap;
1494
1495 fprintf(stderr, "NOT IMPLEMENTED: ");
1496
1497 va_start(ap, format);
1498 vfprintf(stderr, format, ap);
1499 va_end(ap);
1500}
1501
1502/* produce a hex dump */
1503void
1504hexdump(unsigned char *p, unsigned int len)
1505{
1506 unsigned char *line = p;
1507 int i, thisline, offset = 0;
1508
1509 while (offset < len)
1510 {
1511 printf("%04x ", offset);
1512 thisline = len - offset;
1513 if (thisline > 16)
1514 thisline = 16;
1515
1516 for (i = 0; i < thisline; i++)
1517 printf("%02x ", line[i]);
1518
1519 for (; i < 16; i++)
1520 printf(" ");
1521
1522 for (i = 0; i < thisline; i++)
1523 printf("%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.');
1524
1525 printf("\n");
1526 offset += thisline;
1527 line += thisline;
1528 }
1529}
1530
1531/*
1532 input: src is the string we look in for needle.
1533 Needle may be escaped by a backslash, in
1534 that case we ignore that particular needle.
1535 return value: returns next src pointer, for
1536 succesive executions, like in a while loop
1537 if retval is 0, then there are no more args.
1538 pitfalls:
1539 src is modified. 0x00 chars are inserted to
1540 terminate strings.
1541 return val, points on the next val chr after ins
1542 0x00
1543
1544 example usage:
1545 while( (pos = next_arg( optarg, ',')) ){
1546 printf("%s\n",optarg);
1547 optarg=pos;
1548 }
1549
1550*/
1551char *
1552next_arg(char *src, char needle)
1553{
1554 char *nextval;
1555 char *p;
1556 char *mvp = 0;
1557
1558 /* EOS */
1559 if (*src == (char) 0x00)
1560 return 0;
1561
1562 p = src;
1563 /* skip escaped needles */
1564 while ((nextval = strchr(p, needle)))
1565 {
1566 mvp = nextval - 1;
1567 /* found backslashed needle */
1568 if (*mvp == '\\' && (mvp > src))
1569 {
1570 /* move string one to the left */
1571 while (*(mvp + 1) != (char) 0x00)
1572 {
1573 *mvp = *(mvp + 1);
1574 mvp++;
1575 }
1576 *mvp = (char) 0x00;
1577 p = nextval;
1578 }
1579 else
1580 {
1581 p = nextval + 1;
1582 break;
1583 }
1584
1585 }
1586
1587 /* more args available */
1588 if (nextval)
1589 {
1590 *nextval = (char) 0x00;
1591 return ++nextval;
1592 }
1593
1594 /* no more args after this, jump to EOS */
1595 nextval = src + strlen(src);
1596 return nextval;
1597}
1598
1599
1600void
1601toupper_str(char *p)
1602{
1603 while (*p)
1604 {
1605 if ((*p >= 'a') && (*p <= 'z'))
1606 *p = toupper((int) *p);
1607 p++;
1608 }
1609}
1610
1611
1612RD_BOOL
1613str_startswith(const char *s, const char *prefix)
1614{
1615 return (strncmp(s, prefix, strlen(prefix)) == 0);
1616}
1617
1618
1619/* Split input into lines, and call linehandler for each
1620 line. Incomplete lines are saved in the rest variable, which should
1621 initially point to NULL. When linehandler returns False, stop and
1622 return False. Otherwise, return True. */
1623RD_BOOL
1624str_handle_lines(const char *input, char **rest, str_handle_lines_t linehandler, void *data)
1625{
1626 char *buf, *p;
1627 char *oldrest;
1628 size_t inputlen;
1629 size_t buflen;
1630 size_t restlen = 0;
1631 RD_BOOL ret = True;
1632
1633 /* Copy data to buffer */
1634 inputlen = strlen(input);
1635 if (*rest)
1636 restlen = strlen(*rest);
1637 buflen = restlen + inputlen + 1;
1638 buf = (char *) xmalloc(buflen);
1639 buf[0] = '\0';
1640 if (*rest)
1641 STRNCPY(buf, *rest, buflen);
1642 strncat(buf, input, inputlen);
1643 p = buf;
1644
1645 while (1)
1646 {
1647 char *newline = strchr(p, '\n');
1648 if (newline)
1649 {
1650 *newline = '\0';
1651 if (!linehandler(p, data))
1652 {
1653 p = newline + 1;
1654 ret = False;
1655 break;
1656 }
1657 p = newline + 1;
1658 }
1659 else
1660 {
1661 break;
1662
1663 }
1664 }
1665
1666 /* Save in rest */
1667 oldrest = *rest;
1668 restlen = buf + buflen - p;
1669 *rest = (char *) xmalloc(restlen);
1670 STRNCPY((*rest), p, restlen);
1671 xfree(oldrest);
1672
1673 xfree(buf);
1674 return ret;
1675}
1676
1677/* Execute the program specified by argv. For each line in
1678 stdout/stderr output, call linehandler. Returns false on failure. */
1679RD_BOOL
1680subprocess(char *const argv[], str_handle_lines_t linehandler, void *data)
1681{
1682 pid_t child;
1683 int fd[2];
1684 int n = 1;
1685 char output[256];
1686 char *rest = NULL;
1687
1688 if (pipe(fd) < 0)
1689 {
1690 perror("pipe");
1691 return False;
1692 }
1693
1694 if ((child = fork()) < 0)
1695 {
1696 perror("fork");
1697 return False;
1698 }
1699
1700 /* Child */
1701 if (child == 0)
1702 {
1703 /* Close read end */
1704 close(fd[0]);
1705
1706 /* Redirect stdout and stderr to pipe */
1707 dup2(fd[1], 1);
1708 dup2(fd[1], 2);
1709
1710 /* Execute */
1711 execvp(argv[0], argv);
1712 perror("Error executing child");
1713 _exit(128);
1714 }
1715
1716 /* Parent. Close write end. */
1717 close(fd[1]);
1718 while (n > 0)
1719 {
1720 n = read(fd[0], output, 255);
1721 output[n] = '\0';
1722 str_handle_lines(output, &rest, linehandler, data);
1723 }
1724 xfree(rest);
1725
1726 return True;
1727}
1728
1729
1730/* not all clibs got ltoa */
1731#define LTOA_BUFSIZE (sizeof(long) * 8 + 1)
1732
1733char *
1734l_to_a(long N, int base)
1735{
1736 static char ret[LTOA_BUFSIZE];
1737
1738 char *head = ret, buf[LTOA_BUFSIZE], *tail = buf + sizeof(buf);
1739
1740 register int divrem;
1741
1742 if (base < 36 || 2 > base)
1743 base = 10;
1744
1745 if (N < 0)
1746 {
1747 *head++ = '-';
1748 N = -N;
1749 }
1750
1751 tail = buf + sizeof(buf);
1752 *--tail = 0;
1753
1754 do
1755 {
1756 divrem = N % base;
1757 *--tail = (divrem <= 9) ? divrem + '0' : divrem + 'a' - 10;
1758 N /= base;
1759 }
1760 while (N);
1761
1762 strcpy(head, tail);
1763 return ret;
1764}
1765
1766int
1767load_licence(unsigned char **data)
1768{
1769 uint8 ho[20], hi[16];
1770 char *home, path[PATH_MAX], hash[41];
1771 struct stat st;
1772 int fd, length;
1773
1774 home = getenv("HOME");
1775 if (home == NULL)
1776 return -1;
1777
1778 memset(hi, 0, sizeof(hi));
1779 snprintf((char *) hi, 16, "%s", g_hostname);
1780 sec_hash_sha1_16(ho, hi, g_static_rdesktop_salt_16);
1781 sec_hash_to_string(hash, sizeof(hash), ho, sizeof(ho));
1782
1783 snprintf(path, PATH_MAX, "%s" RDESKTOP_LICENSE_STORE "/%s.cal", home, hash);
1784 path[sizeof(path) - 1] = '\0';
1785
1786 fd = open(path, O_RDONLY);
1787 if (fd == -1)
1788 {
1789 /* fallback to try reading old license file */
1790 snprintf(path, PATH_MAX, "%s/.rdesktop/license.%s", home, g_hostname);
1791 path[sizeof(path) - 1] = '\0';
1792 if ((fd = open(path, O_RDONLY)) == -1)
1793 return -1;
1794 }
1795
1796 if (fstat(fd, &st))
1797 {
1798 close(fd);
1799 return -1;
1800 }
1801
1802 *data = (uint8 *) xmalloc(st.st_size);
1803 length = read(fd, *data, st.st_size);
1804 close(fd);
1805 return length;
1806}
1807
1808void
1809save_licence(unsigned char *data, int length)
1810{
1811 uint8 ho[20], hi[16];
1812 char *home, path[PATH_MAX], tmppath[PATH_MAX], hash[41];
1813 int fd;
1814
1815 home = getenv("HOME");
1816 if (home == NULL)
1817 return;
1818
1819 snprintf(path, PATH_MAX, "%s" RDESKTOP_LICENSE_STORE, home);
1820 path[sizeof(path) - 1] = '\0';
1821 if (utils_mkdir_p(path, 0700) == -1)
1822 {
1823 perror(path);
1824 return;
1825 }
1826
1827 memset(hi, 0, sizeof(hi));
1828 snprintf((char *) hi, 16, "%s", g_hostname);
1829 sec_hash_sha1_16(ho, hi, g_static_rdesktop_salt_16);
1830 sec_hash_to_string(hash, sizeof(hash), ho, sizeof(ho));
1831
1832 /* write licence to {sha1}.cal.new, then atomically
1833 rename to {sha1}.cal */
1834 snprintf(path, PATH_MAX, "%s" RDESKTOP_LICENSE_STORE "/%s.cal", home, hash);
1835 path[sizeof(path) - 1] = '\0';
1836
1837 snprintf(tmppath, PATH_MAX, "%s.new", path);
1838 path[sizeof(path) - 1] = '\0';
1839
1840 fd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1841 if (fd == -1)
1842 {
1843 perror(tmppath);
1844 return;
1845 }
1846
1847 if (write(fd, data, length) != length)
1848 {
1849 perror(tmppath);
1850 unlink(tmppath);
1851 }
1852 else if (rename(tmppath, path) == -1)
1853 {
1854 perror(path);
1855 unlink(tmppath);
1856 }
1857
1858 close(fd);
1859}
1860
1861/* create rdesktop ui */
1862void
1863rd_create_ui()
1864{
1865 /* only create a window if we dont have one intialized */
1866 if (!ui_have_window())
1867 {
1868 if (!ui_create_window())
1869 exit(EX_OSERR);
1870 }
1871}
1872
1873/* Create the bitmap cache directory */
1874RD_BOOL
1875rd_pstcache_mkdir(void)
1876{
1877 char *home;
1878 char bmpcache_dir[256];
1879
1880 home = getenv("HOME");
1881
1882 if (home == NULL)
1883 return False;
1884
1885#ifdef VBOX
1886 snprintf(bmpcache_dir, sizeof(bmpcache_dir), "%s/%s", home, ".rdesktop");
1887#else
1888 sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop");
1889#endif
1890
1891 if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST)
1892 {
1893 perror(bmpcache_dir);
1894 return False;
1895 }
1896
1897#ifdef VBOX
1898 snprintf(bmpcache_dir, sizeof(bmpcache_dir), "%s/%s", home, ".rdesktop/cache");
1899#else
1900 sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop/cache");
1901#endif
1902
1903 if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST)
1904 {
1905 perror(bmpcache_dir);
1906 return False;
1907 }
1908
1909 return True;
1910}
1911
1912/* open a file in the .rdesktop directory */
1913int
1914rd_open_file(char *filename)
1915{
1916 char *home;
1917 char fn[256];
1918 int fd;
1919
1920 home = getenv("HOME");
1921 if (home == NULL)
1922 return -1;
1923#ifdef VBOX
1924 snprintf(fn, sizeof(fn), "%s/.rdesktop/%s", home, filename);
1925#else
1926 sprintf(fn, "%s/.rdesktop/%s", home, filename);
1927#endif
1928 fd = open(fn, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
1929 if (fd == -1)
1930 perror(fn);
1931 return fd;
1932}
1933
1934/* close file */
1935void
1936rd_close_file(int fd)
1937{
1938 close(fd);
1939}
1940
1941/* read from file*/
1942int
1943rd_read_file(int fd, void *ptr, int len)
1944{
1945 return read(fd, ptr, len);
1946}
1947
1948/* write to file */
1949int
1950rd_write_file(int fd, void *ptr, int len)
1951{
1952 return write(fd, ptr, len);
1953}
1954
1955/* move file pointer */
1956int
1957rd_lseek_file(int fd, int offset)
1958{
1959 return lseek(fd, offset, SEEK_SET);
1960}
1961
1962/* do a write lock on a file */
1963RD_BOOL
1964rd_lock_file(int fd, int start, int len)
1965{
1966 struct flock lock;
1967
1968 lock.l_type = F_WRLCK;
1969 lock.l_whence = SEEK_SET;
1970 lock.l_start = start;
1971 lock.l_len = len;
1972 if (fcntl(fd, F_SETLK, &lock) == -1)
1973 return False;
1974 return True;
1975}
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