1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Entrypoint and utility functions
4 Copyright (C) Matthew Chapman 1999-2005
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 2 of the License, or
9 (at your option) any later version.
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.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include <stdarg.h> /* va_list va_start va_end */
22 #include <unistd.h> /* read close getuid getgid getpid getppid gethostname */
23 #include <fcntl.h> /* open */
24 #include <pwd.h> /* getpwuid */
25 #include <termios.h> /* tcgetattr tcsetattr */
26 #include <sys/stat.h> /* stat */
27 #include <sys/time.h> /* gettimeofday */
28 #include <sys/times.h> /* times */
29 #include <ctype.h> /* toupper */
37 #ifdef HAVE_LANGINFO_H
43 #include <sys/types.h>
44 #include <sys/socket.h> /* socket connect */
45 #include <sys/un.h> /* sockaddr_un */
48 #include <openssl/md5.h>
52 rdp2vnc_connect(char *server
, uint32 flags
, char *domain
, char *password
,
53 char *shell
, char *directory
);
55 /* Display usage information */
59 fprintf(stderr
, "rdesktop: A Remote Desktop Protocol client.\n");
60 fprintf(stderr
, "Version " VERSION
". Copyright (C) 1999-2005 Matt Chapman.\n");
61 fprintf(stderr
, "See http://www.rdesktop.org/ for more information.\n\n");
63 fprintf(stderr
, "Usage: %s [options] server[:port]\n", program
);
65 fprintf(stderr
, " -V: vnc port\n");
66 fprintf(stderr
, " -Q: defer time (ms)\n");
68 fprintf(stderr
, " -u: user name\n");
69 fprintf(stderr
, " -d: domain\n");
70 fprintf(stderr
, " -s: shell\n");
71 fprintf(stderr
, " -c: working directory\n");
72 fprintf(stderr
, " -p: password (- to prompt)\n");
73 fprintf(stderr
, " -n: client hostname\n");
74 fprintf(stderr
, " -k: keyboard layout on server (en-us, de, sv, etc.)\n");
75 fprintf(stderr
, " -g: desktop geometry (WxH)\n");
76 fprintf(stderr
, " -f: full-screen mode\n");
77 fprintf(stderr
, " -b: force bitmap updates\n");
79 fprintf(stderr
, " -L: local codepage\n");
81 fprintf(stderr
, " -A: enable SeamlessRDP mode\n");
82 fprintf(stderr
, " -B: use BackingStore of X-server (if available)\n");
83 fprintf(stderr
, " -e: disable encryption (French TS)\n");
84 fprintf(stderr
, " -E: disable encryption from client to server\n");
85 fprintf(stderr
, " -m: do not send motion events\n");
86 fprintf(stderr
, " -C: use private colour map\n");
87 fprintf(stderr
, " -D: hide window manager decorations\n");
88 fprintf(stderr
, " -K: keep window manager key bindings\n");
89 fprintf(stderr
, " -S: caption button size (single application mode)\n");
90 fprintf(stderr
, " -T: window title\n");
91 fprintf(stderr
, " -N: enable numlock syncronization\n");
92 fprintf(stderr
, " -X: embed into another window with a given id.\n");
93 fprintf(stderr
, " -a: connection colour depth\n");
94 fprintf(stderr
, " -z: enable rdp compression\n");
95 fprintf(stderr
, " -x: RDP5 experience (m[odem 28.8], b[roadband], l[an] or hex nr.)\n");
96 fprintf(stderr
, " -P: use persistent bitmap caching\n");
97 fprintf(stderr
, " -r: enable specified device redirection (this flag can be repeated)\n");
99 " '-r comport:COM1=/dev/ttyS0': enable serial redirection of /dev/ttyS0 to COM1\n");
100 fprintf(stderr
, " or COM1=/dev/ttyS0,COM2=/dev/ttyS1\n");
102 " '-r disk:floppy=/mnt/floppy': enable redirection of /mnt/floppy to 'floppy' share\n");
103 fprintf(stderr
, " or 'floppy=/mnt/floppy,cdrom=/mnt/cdrom'\n");
104 fprintf(stderr
, " '-r clientname=<client name>': Set the client name displayed\n");
105 fprintf(stderr
, " for redirected disks\n");
107 " '-r lptport:LPT1=/dev/lp0': enable parallel redirection of /dev/lp0 to LPT1\n");
108 fprintf(stderr
, " or LPT1=/dev/lp0,LPT2=/dev/lp1\n");
109 fprintf(stderr
, " '-r printer:mydeskjet': enable printer redirection\n");
111 " or mydeskjet=\"HP LaserJet IIIP\" to enter server driver as well\n");
112 fprintf(stderr
, " '-r sound:[local|off|remote]': enable sound redirection\n");
113 fprintf(stderr
, " remote would leave sound on server\n");
115 " '-r clipboard:[off|PRIMARYCLIPBOARD|CLIPBOARD]': enable clipboard\n");
116 fprintf(stderr
, " redirection.\n");
118 " 'PRIMARYCLIPBOARD' looks at both PRIMARY and CLIPBOARD\n");
119 fprintf(stderr
, " when sending data to server.\n");
120 fprintf(stderr
, " 'CLIPBOARD' looks at only CLIPBOARD.\n");
121 fprintf(stderr
, " -0: attach to console\n");
122 fprintf(stderr
, " -4: use RDP version 4\n");
123 fprintf(stderr
, " -5: use RDP version 5 (default)\n");
127 print_disconnect_reason(uint16 reason
)
133 case exDiscReasonNoInfo
:
134 text
= "No information available";
137 case exDiscReasonAPIInitiatedDisconnect
:
138 text
= "Server initiated disconnect";
141 case exDiscReasonAPIInitiatedLogoff
:
142 text
= "Server initiated logoff";
145 case exDiscReasonServerIdleTimeout
:
146 text
= "Server idle timeout reached";
149 case exDiscReasonServerLogonTimeout
:
150 text
= "Server logon timeout reached";
153 case exDiscReasonReplacedByOtherConnection
:
154 text
= "The session was replaced";
157 case exDiscReasonOutOfMemory
:
158 text
= "The server is out of memory";
161 case exDiscReasonServerDeniedConnection
:
162 text
= "The server denied the connection";
165 case exDiscReasonServerDeniedConnectionFips
:
166 text
= "The server denied the connection for security reason";
169 case exDiscReasonLicenseInternal
:
170 text
= "Internal licensing error";
173 case exDiscReasonLicenseNoLicenseServer
:
174 text
= "No license server available";
177 case exDiscReasonLicenseNoLicense
:
178 text
= "No valid license available";
181 case exDiscReasonLicenseErrClientMsg
:
182 text
= "Invalid licensing message";
185 case exDiscReasonLicenseHwidDoesntMatchLicense
:
186 text
= "Hardware id doesn't match software license";
189 case exDiscReasonLicenseErrClientLicense
:
190 text
= "Client license error";
193 case exDiscReasonLicenseCantFinishProtocol
:
194 text
= "Network error during licensing protocol";
197 case exDiscReasonLicenseClientEndedProtocol
:
198 text
= "Licensing protocol was not completed";
201 case exDiscReasonLicenseErrClientEncryption
:
202 text
= "Incorrect client license enryption";
205 case exDiscReasonLicenseCantUpgradeLicense
:
206 text
= "Can't upgrade license";
209 case exDiscReasonLicenseNoRemoteConnections
:
210 text
= "The server is not licensed to accept remote connections";
214 if (reason
> 0x1000 && reason
< 0x7fff)
216 text
= "Internal protocol error";
220 text
= "Unknown reason";
223 fprintf(stderr
, "disconnect: %s.\n", text
);
227 rdesktop_reset_state(RDPCLIENT
* This
)
229 rdp_reset_state(This
);
233 read_password(char *password
, int size
)
240 if (tcgetattr(STDIN_FILENO
, &tios
) == 0)
242 fprintf(stderr
, "Password: ");
243 tios
.c_lflag
&= ~ECHO
;
244 tcsetattr(STDIN_FILENO
, TCSANOW
, &tios
);
248 if (fgets(password
, size
, stdin
) != NULL
)
252 /* strip final newline */
253 p
= strchr(password
, '\n');
260 tios
.c_lflag
|= ECHO
;
261 tcsetattr(STDIN_FILENO
, TCSANOW
, &tios
);
262 fprintf(stderr
, "\n");
269 parse_server_and_port(RDPCLIENT
* This
, char *server
)
282 if (addr_colons
>= 2)
284 /* numeric IPv6 style address format - [1:2:3::4]:port */
285 p
= strchr(server
, ']');
286 if (*server
== '[' && p
!= NULL
)
288 if (*(p
+ 1) == ':' && *(p
+ 2) != '\0')
289 This
->tcp_port_rdp
= strtol(p
+ 2, NULL
, 10);
290 /* remove the port number and brackets from the address */
292 strncpy(server
, server
+ 1, strlen(server
));
297 /* dns name or IPv4 style address format - server.example.com:port or 1.2.3.4:port */
298 p
= strchr(server
, ':');
301 This
->tcp_port_rdp
= strtol(p
+ 1, NULL
, 10);
305 #else /* no IPv6 support */
306 p
= strchr(server
, ':');
309 This
->tcp_port_rdp
= strtol(p
+ 1, NULL
, 10);
318 main(int argc
, char *argv
[])
321 char fullhostname
[64];
326 BOOL prompt_password
, deactivated
;
328 uint32 flags
, ext_disc_reason
= 0;
332 int username_option
= 0;
333 BOOL geometry_option
= False
;
334 int run_count
= 0; /* Session Directory support */
335 BOOL continue_connect
= True
; /* Session Directory support */
338 This
= xmalloc(sizeof(RDPCLIENT
));
339 memset(This
, 0, sizeof(RDPCLIENT
));
341 This
->keylayout
= 0x409; /* Defaults to US keyboard layout */
342 This
->keyboard_type
= 0x4; /* Defaults to US keyboard layout */
343 This
->keyboard_subtype
= 0x0; /* Defaults to US keyboard layout */
344 This
->keyboard_functionkeys
= 0xc; /* Defaults to US keyboard layout */
345 This
->width
= 800; /* width is special: If 0, the
346 geometry will be fetched from
347 _NET_WORKAREA. If negative,
348 absolute value specifies the
349 percent of the whole screen. */
351 This
->server_depth
= -1;
352 This
->bitmap_compression
= True
;
353 This
->sendmotion
= True
;
354 This
->bitmap_cache
= True
;
355 This
->bitmap_cache_persist_enable
= False
;
356 This
->bitmap_cache_precache
= True
;
357 This
->encryption
= True
;
358 This
->packet_encryption
= True
;
359 This
->desktop_save
= True
; /* desktop save order */
360 This
->polygon_ellipse_orders
= True
; /* polygon / ellipse orders */
361 This
->fullscreen
= False
;
362 This
->grab_keyboard
= True
;
363 This
->hide_decorations
= False
;
364 This
->use_rdp5
= True
;
365 This
->rdpclip
= True
;
366 This
->console_session
= False
;
367 This
->numlock_sync
= False
;
368 This
->lspci_enabled
= False
;
369 This
->owncolmap
= False
;
370 This
->ownbackstore
= True
; /* We can't rely on external BackingStore */
371 This
->seamless_rdp
= False
;
372 This
->rdp5_performanceflags
= RDP5_NO_WALLPAPER
| RDP5_NO_FULLWINDOWDRAG
| RDP5_NO_MENUANIMATIONS
;
373 This
->tcp_port_rdp
= TCP_PORT_RDP
;
376 This
->cache
.bmpcache_lru
[0] = NOT_SET
;
377 This
->cache
.bmpcache_lru
[1] = NOT_SET
;
378 This
->cache
.bmpcache_lru
[2] = NOT_SET
;
379 This
->cache
.bmpcache_mru
[0] = NOT_SET
;
380 This
->cache
.bmpcache_mru
[1] = NOT_SET
;
381 This
->cache
.bmpcache_mru
[2] = NOT_SET
;
384 This
->rdp
.iconv_works
= True
;
387 This
->xclip
.auto_mode
= True
;
390 /* Set locale according to environment */
391 locale
= setlocale(LC_ALL
, "");
394 locale
= xstrdup(locale
);
398 flags
= RDP_LOGON_NORMAL
;
399 prompt_password
= False
;
400 domain
[0] = password
[0] = shell
[0] = directory
[0] = 0;
403 This
->num_devices
= 0;
406 #define VNCOPT "V:Q:"
411 while ((c
= getopt(argc
, argv
,
412 VNCOPT
"Au:L:d:s:c:p:n:k:g:fbBeEmzCDKS:T:NX:a:x:Pr:045h?")) != -1)
418 This
->rfb_port
= strtol(optarg
, NULL
, 10);
419 if (This
->rfb_port
< 100)
420 This
->rfb_port
+= 5900;
424 This
->defer_time
= strtol(optarg
, NULL
, 10);
425 if (This
->defer_time
< 0)
426 This
->defer_time
= 0;
431 This
->seamless_rdp
= True
;
435 STRNCPY(This
->username
, optarg
, sizeof(This
->username
));
441 STRNCPY(This
->codepage
, optarg
, sizeof(This
->codepage
));
443 error("iconv support not available\n");
448 STRNCPY(domain
, optarg
, sizeof(domain
));
452 STRNCPY(shell
, optarg
, sizeof(shell
));
456 STRNCPY(directory
, optarg
, sizeof(directory
));
460 if ((optarg
[0] == '-') && (optarg
[1] == 0))
462 prompt_password
= True
;
466 STRNCPY(password
, optarg
, sizeof(password
));
467 flags
|= RDP_LOGON_AUTO
;
469 /* try to overwrite argument so it won't appear in ps */
476 STRNCPY(This
->hostname
, optarg
, sizeof(This
->hostname
));
480 STRNCPY(This
->keymapname
, optarg
, sizeof(This
->keymapname
));
484 geometry_option
= True
;
485 This
->fullscreen
= False
;
486 if (!strcmp(optarg
, "workarea"))
488 This
->width
= This
->height
= 0;
492 This
->width
= strtol(optarg
, &p
, 10);
493 if (This
->width
<= 0)
495 error("invalid geometry\n");
500 This
->height
= strtol(p
+ 1, &p
, 10);
502 if (This
->height
<= 0)
504 error("invalid geometry\n");
510 This
->width
= -This
->width
;
514 if (*p
== '+' || *p
== '-')
516 This
->pos
|= (*p
== '-') ? 2 : 1;
517 This
->xpos
= strtol(p
, &p
, 10);
520 if (*p
== '+' || *p
== '-')
522 This
->pos
|= (*p
== '-') ? 4 : 1;
523 This
->ypos
= strtol(p
, NULL
, 10);
529 This
->fullscreen
= True
;
533 This
->bitmap_cache
= False
;
537 This
->ownbackstore
= False
;
541 This
->encryption
= False
;
544 This
->packet_encryption
= False
;
547 This
->sendmotion
= False
;
551 This
->owncolmap
= True
;
555 This
->hide_decorations
= True
;
559 This
->grab_keyboard
= False
;
563 if (!strcmp(optarg
, "standard"))
565 This
->win_button_size
= 18;
569 This
->win_button_size
= strtol(optarg
, &p
, 10);
573 error("invalid button size\n");
580 STRNCPY(This
->title
, optarg
, sizeof(This
->title
));
584 This
->numlock_sync
= True
;
588 This
->embed_wnd
= strtol(optarg
, NULL
, 0);
592 This
->server_depth
= strtol(optarg
, NULL
, 10);
593 if (This
->server_depth
!= 8 &&
594 This
->server_depth
!= 16 &&
595 This
->server_depth
!= 15 && This
->server_depth
!= 24)
597 error("Invalid server colour depth.\n");
603 DEBUG(("rdp compression enabled\n"));
604 flags
|= (RDP_LOGON_COMPRESSION
| RDP_LOGON_COMPRESSION2
);
608 if (str_startswith(optarg
, "m")) /* modem */
610 This
->rdp5_performanceflags
=
611 RDP5_NO_WALLPAPER
| RDP5_NO_FULLWINDOWDRAG
|
612 RDP5_NO_MENUANIMATIONS
| RDP5_NO_THEMING
;
614 else if (str_startswith(optarg
, "b")) /* broadband */
616 This
->rdp5_performanceflags
= RDP5_NO_WALLPAPER
;
618 else if (str_startswith(optarg
, "l")) /* lan */
620 This
->rdp5_performanceflags
= RDP5_DISABLE_NOTHING
;
624 This
->rdp5_performanceflags
= strtol(optarg
, NULL
, 16);
629 This
->bitmap_cache_persist_enable
= True
;
634 if (str_startswith(optarg
, "sound"))
641 while ((p
= next_arg(optarg
, ',')))
643 if (str_startswith(optarg
, "remote"))
644 flags
|= RDP_LOGON_LEAVE_AUDIO
;
646 if (str_startswith(optarg
, "local"))
648 This
->rdpsnd_enabled
= True
;
650 warning("Not compiled with sound support\n");
653 if (str_startswith(optarg
, "off"))
655 This
->rdpsnd_enabled
= False
;
657 warning("Not compiled with sound support\n");
666 This
->rdpsnd_enabled
= True
;
668 warning("Not compiled with sound support\n");
672 else if (str_startswith(optarg
, "disk"))
674 /* -r disk:h:=/mnt/floppy */
675 disk_enum_devices(This
, &This
->num_devices
, optarg
+ 4);
677 else if (str_startswith(optarg
, "comport"))
679 serial_enum_devices(This
, &This
->num_devices
, optarg
+ 7);
681 else if (str_startswith(optarg
, "lspci"))
683 This
->lspci_enabled
= True
;
685 else if (str_startswith(optarg
, "lptport"))
687 parallel_enum_devices(This
, &This
->num_devices
, optarg
+ 7);
689 else if (str_startswith(optarg
, "printer"))
691 printer_enum_devices(This
, &This
->num_devices
, optarg
+ 7);
693 else if (str_startswith(optarg
, "clientname"))
695 This
->rdpdr_clientname
= xmalloc(strlen(optarg
+ 11) + 1);
696 strcpy(This
->rdpdr_clientname
, optarg
+ 11);
698 else if (str_startswith(optarg
, "clipboard"))
706 if (str_startswith(optarg
, "off"))
707 This
->rdpclip
= False
;
709 cliprdr_set_mode(This
, optarg
);
712 This
->rdpclip
= True
;
716 warning("Unknown -r argument\n\n\tPossible arguments are: comport, disk, lptport, printer, sound, clipboard\n");
721 This
->console_session
= True
;
725 This
->use_rdp5
= False
;
729 This
->use_rdp5
= True
;
740 if (argc
- optind
!= 1)
746 STRNCPY(server
, argv
[optind
], sizeof(server
));
747 parse_server_and_port(This
, server
);
749 if (This
->seamless_rdp
)
751 if (This
->win_button_size
)
753 error("You cannot use -S and -A at the same time\n");
756 This
->rdp5_performanceflags
&= ~RDP5_NO_FULLWINDOWDRAG
;
759 error("You cannot use -g and -A at the same time\n");
762 if (This
->fullscreen
)
764 error("You cannot use -f and -A at the same time\n");
767 if (This
->hide_decorations
)
769 error("You cannot use -D and -A at the same time\n");
774 error("You cannot use -X and -A at the same time\n");
779 error("You cannot use -4 and -A at the same time\n");
783 This
->grab_keyboard
= False
;
786 if (!username_option
)
788 pw
= getpwuid(getuid());
789 if ((pw
== NULL
) || (pw
->pw_name
== NULL
))
791 error("could not determine username, use -u\n");
795 STRNCPY(This
->username
, pw
->pw_name
, sizeof(This
->username
));
799 if (This
->codepage
[0] == 0)
801 if (setlocale(LC_CTYPE
, ""))
803 STRNCPY(This
->codepage
, nl_langinfo(CODESET
), sizeof(This
->codepage
));
807 STRNCPY(This
->codepage
, DEFAULT_CODEPAGE
, sizeof(This
->codepage
));
812 if (This
->hostname
[0] == 0)
814 if (gethostname(fullhostname
, sizeof(fullhostname
)) == -1)
816 error("could not determine local hostname, use -n\n");
820 p
= strchr(fullhostname
, '.');
824 STRNCPY(This
->hostname
, fullhostname
, sizeof(This
->hostname
));
827 if (This
->keymapname
[0] == 0)
829 if (locale
&& xkeymap_from_locale(This
, locale
))
831 fprintf(stderr
, "Autoselected keyboard map %s\n", This
->keymapname
);
835 STRNCPY(This
->keymapname
, "en-us", sizeof(This
->keymapname
));
842 if (prompt_password
&& read_password(password
, sizeof(password
)))
843 flags
|= RDP_LOGON_AUTO
;
845 if (This
->title
[0] == 0)
847 strcpy(This
->title
, "rdesktop - ");
848 strncat(This
->title
, server
, sizeof(This
->title
) - sizeof("rdesktop - "));
852 rdp2vnc_connect(server
, flags
, domain
, password
, shell
, directory
);
860 if (This
->rdpsnd_enabled
)
864 if (This
->lspci_enabled
)
869 while (run_count
< 2 && continue_connect
) /* add support for Session Directory; only reconnect once */
873 if (!rdp_connect(This
, server
, flags
, domain
, password
, shell
, directory
))
876 else if (!rdp_reconnect
877 (This
, server
, flags
, domain
, password
, shell
, directory
, This
->redirect_cookie
))
880 /* By setting encryption to False here, we have an encrypted login
881 packet but unencrypted transfer of other packets */
882 if (!This
->packet_encryption
)
883 This
->encryption
= False
;
886 DEBUG(("Connection successful.\n"));
887 memset(password
, 0, sizeof(password
));
890 if (!ui_create_window(This
))
891 continue_connect
= False
;
893 if (continue_connect
)
894 rdp_main_loop(This
, &deactivated
, &ext_disc_reason
);
896 DEBUG(("Disconnecting...\n"));
897 rdp_disconnect(This
);
899 if ((This
->redirect
== True
) && (run_count
== 0)) /* Support for Session Directory */
901 /* reset state of major globals */
902 rdesktop_reset_state(This
);
904 STRNCPY(domain
, This
->redirect_domain
, sizeof(domain
));
905 STRNCPY(This
->username
, This
->redirect_username
, sizeof(This
->username
));
906 STRNCPY(password
, This
->redirect_password
, sizeof(password
));
907 STRNCPY(server
, This
->redirect_server
, sizeof(server
));
908 flags
|= RDP_LOGON_AUTO
;
910 This
->redirect
= False
;
914 continue_connect
= False
;
915 ui_destroy_window(This
);
922 cache_save_state(This
);
925 if (ext_disc_reason
>= 2)
926 print_disconnect_reason(ext_disc_reason
);
930 /* clean disconnect */
935 if (ext_disc_reason
== exDiscReasonAPIInitiatedDisconnect
936 || ext_disc_reason
== exDiscReasonAPIInitiatedLogoff
)
938 /* not so clean disconnect, but nothing to worry about */
953 /* Read 32 random bytes from PRNGD or EGD socket (based on OpenSSL RAND_egd) */
955 generate_random_egd(uint8
* buf
)
957 struct sockaddr_un addr
;
961 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
965 addr
.sun_family
= AF_UNIX
;
966 memcpy(addr
.sun_path
, EGD_SOCKET
, sizeof(EGD_SOCKET
));
967 if (connect(fd
, (struct sockaddr
*) &addr
, sizeof(addr
)) == -1)
970 /* PRNGD and EGD use a simple communications protocol */
971 buf
[0] = 1; /* Non-blocking (similar to /dev/urandom) */
972 buf
[1] = 32; /* Number of requested random bytes */
973 if (write(fd
, buf
, 2) != 2)
976 if ((read(fd
, buf
, 1) != 1) || (buf
[0] == 0)) /* Available? */
979 if (read(fd
, buf
, 32) != 32)
990 /* Generate a 32-byte random for the secure transport code. */
992 generate_random(uint8
* random
)
1000 /* If we have a kernel random device, try that first */
1001 if (((fd
= open("/dev/urandom", O_RDONLY
)) != -1)
1002 || ((fd
= open("/dev/random", O_RDONLY
)) != -1))
1004 n
= read(fd
, random
, 32);
1011 /* As a second preference use an EGD */
1012 if (generate_random_egd(random
))
1016 /* Otherwise use whatever entropy we can gather - ideas welcome. */
1017 r
= (uint32
*) random
;
1018 r
[0] = (getpid()) | (getppid() << 16);
1019 r
[1] = (getuid()) | (getgid() << 16);
1020 r
[2] = times(&tmsbuf
); /* system uptime (clocks) */
1021 gettimeofday((struct timeval
*) &r
[3], NULL
); /* sec and usec */
1027 /* Hash both halves with MD5 to obscure possible patterns */
1029 MD5_Update(&md5
, random
, 16);
1030 MD5_Final(random
, &md5
);
1031 MD5_Update(&md5
, random
+ 16, 16);
1032 MD5_Final(random
+ 16, &md5
);
1035 /* malloc; exit if out of memory */
1039 void *mem
= malloc(size
);
1042 error("xmalloc %d\n", size
);
1050 xstrdup(const char *s
)
1052 char *mem
= strdup(s
);
1061 /* realloc; exit if out of memory */
1063 xrealloc(void *oldmem
, int size
)
1069 mem
= realloc(oldmem
, size
);
1072 error("xrealloc %d\n", size
);
1085 /* report an error */
1087 error(char *format
, ...)
1091 fprintf(stderr
, "ERROR: ");
1093 va_start(ap
, format
);
1094 vfprintf(stderr
, format
, ap
);
1098 /* report a warning */
1100 warning(char *format
, ...)
1104 fprintf(stderr
, "WARNING: ");
1106 va_start(ap
, format
);
1107 vfprintf(stderr
, format
, ap
);
1111 /* report an unimplemented protocol feature */
1113 unimpl(char *format
, ...)
1117 fprintf(stderr
, "NOT IMPLEMENTED: ");
1119 va_start(ap
, format
);
1120 vfprintf(stderr
, format
, ap
);
1124 /* produce a hex dump */
1126 hexdump(unsigned char *p
, unsigned int len
)
1128 unsigned char *line
= p
;
1129 int i
, thisline
, offset
= 0;
1131 while (offset
< len
)
1133 printf("%04x ", offset
);
1134 thisline
= len
- offset
;
1138 for (i
= 0; i
< thisline
; i
++)
1139 printf("%02x ", line
[i
]);
1144 for (i
= 0; i
< thisline
; i
++)
1145 printf("%c", (line
[i
] >= 0x20 && line
[i
] < 0x7f) ? line
[i
] : '.');
1154 input: src is the string we look in for needle.
1155 Needle may be escaped by a backslash, in
1156 that case we ignore that particular needle.
1157 return value: returns next src pointer, for
1158 succesive executions, like in a while loop
1159 if retval is 0, then there are no more args.
1161 src is modified. 0x00 chars are inserted to
1163 return val, points on the next val chr after ins
1167 while( (pos = next_arg( optarg, ',')) ){
1168 printf("%s\n",optarg);
1174 next_arg(char *src
, char needle
)
1181 if (*src
== (char) 0x00)
1185 /* skip escaped needles */
1186 while ((nextval
= strchr(p
, needle
)))
1189 /* found backslashed needle */
1190 if (*mvp
== '\\' && (mvp
> src
))
1192 /* move string one to the left */
1193 while (*(mvp
+ 1) != (char) 0x00)
1209 /* more args available */
1212 *nextval
= (char) 0x00;
1216 /* no more args after this, jump to EOS */
1217 nextval
= src
+ strlen(src
);
1223 toupper_str(char *p
)
1227 if ((*p
>= 'a') && (*p
<= 'z'))
1228 *p
= toupper((int) *p
);
1235 str_startswith(const char *s
, const char *prefix
)
1237 return (strncmp(s
, prefix
, strlen(prefix
)) == 0);
1241 /* Split input into lines, and call linehandler for each
1242 line. Incomplete lines are saved in the rest variable, which should
1243 initially point to NULL. When linehandler returns False, stop and
1244 return False. Otherwise, return True. */
1246 str_handle_lines(RDPCLIENT
* This
, const char *input
, char **rest
, str_handle_lines_t linehandler
, void *data
)
1255 /* Copy data to buffer */
1256 inputlen
= strlen(input
);
1258 restlen
= strlen(*rest
);
1259 buflen
= restlen
+ inputlen
+ 1;
1260 buf
= (char *) xmalloc(buflen
);
1263 STRNCPY(buf
, *rest
, buflen
);
1264 strncat(buf
, input
, inputlen
);
1269 char *newline
= strchr(p
, '\n');
1273 if (!linehandler(This
, p
, data
))
1290 restlen
= buf
+ buflen
- p
;
1291 *rest
= (char *) xmalloc(restlen
);
1292 STRNCPY((*rest
), p
, restlen
);
1299 /* Execute the program specified by argv. For each line in
1300 stdout/stderr output, call linehandler. Returns false on failure. */
1302 subprocess(RDPCLIENT
* This
, char *const argv
[], str_handle_lines_t linehandler
, void *data
)
1316 if ((child
= fork()) < 0)
1325 /* Close read end */
1328 /* Redirect stdout and stderr to pipe */
1333 execvp(argv
[0], argv
);
1334 perror("Error executing child");
1338 /* Parent. Close write end. */
1342 n
= read(fd
[0], output
, 255);
1344 str_handle_lines(This
, output
, &rest
, linehandler
, data
);
1352 /* not all clibs got ltoa */
1353 #define LTOA_BUFSIZE (sizeof(long) * 8 + 1)
1356 l_to_a(long N
, int base
)
1358 static char ret
[LTOA_BUFSIZE
];
1360 char *head
= ret
, buf
[LTOA_BUFSIZE
], *tail
= buf
+ sizeof(buf
);
1362 register int divrem
;
1364 if (base
< 36 || 2 > base
)
1373 tail
= buf
+ sizeof(buf
);
1379 *--tail
= (divrem
<= 9) ? divrem
+ '0' : divrem
+ 'a' - 10;
1390 load_licence(RDPCLIENT
* This
, unsigned char **data
)
1396 home
= getenv("HOME");
1400 path
= (char *) xmalloc(strlen(home
) + strlen(This
->hostname
) + sizeof("/.rdesktop/licence."));
1401 sprintf(path
, "%s/.rdesktop/licence.%s", home
, This
->hostname
);
1403 fd
= open(path
, O_RDONLY
);
1410 *data
= (uint8
*) xmalloc(st
.st_size
);
1411 length
= read(fd
, *data
, st
.st_size
);
1418 save_licence(RDPCLIENT
* This
, unsigned char *data
, int length
)
1420 char *home
, *path
, *tmppath
;
1423 home
= getenv("HOME");
1427 path
= (char *) xmalloc(strlen(home
) + strlen(This
->hostname
) + sizeof("/.rdesktop/licence."));
1429 sprintf(path
, "%s/.rdesktop", home
);
1430 if ((mkdir(path
, 0700) == -1) && errno
!= EEXIST
)
1436 /* write licence to licence.hostname.new, then atomically rename to licence.hostname */
1438 sprintf(path
, "%s/.rdesktop/licence.%s", home
, This
->hostname
);
1439 tmppath
= (char *) xmalloc(strlen(path
) + sizeof(".new"));
1440 strcpy(tmppath
, path
);
1441 strcat(tmppath
, ".new");
1443 fd
= open(tmppath
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
1450 if (write(fd
, data
, length
) != length
)
1455 else if (rename(tmppath
, path
) == -1)
1466 /* Create the bitmap cache directory */
1468 rd_pstcache_mkdir(void)
1471 char bmpcache_dir
[256];
1473 home
= getenv("HOME");
1478 sprintf(bmpcache_dir
, "%s/%s", home
, ".rdesktop");
1480 if ((mkdir(bmpcache_dir
, S_IRWXU
) == -1) && errno
!= EEXIST
)
1482 perror(bmpcache_dir
);
1486 sprintf(bmpcache_dir
, "%s/%s", home
, ".rdesktop/cache");
1488 if ((mkdir(bmpcache_dir
, S_IRWXU
) == -1) && errno
!= EEXIST
)
1490 perror(bmpcache_dir
);
1497 /* open a file in the .rdesktop directory */
1499 rd_open_file(char *filename
)
1505 home
= getenv("HOME");
1508 sprintf(fn
, "%s/.rdesktop/%s", home
, filename
);
1509 fd
= open(fn
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
);
1517 rd_close_file(int fd
)
1524 rd_read_file(int fd
, void *ptr
, int len
)
1526 return read(fd
, ptr
, len
);
1531 rd_write_file(int fd
, void *ptr
, int len
)
1533 return write(fd
, ptr
, len
);
1536 /* move file pointer */
1538 rd_lseek_file(int fd
, int offset
)
1540 return lseek(fd
, offset
, SEEK_SET
);
1543 /* do a write lock on a file */
1545 rd_lock_file(int fd
, int start
, int len
)
1549 lock
.l_type
= F_WRLCK
;
1550 lock
.l_whence
= SEEK_SET
;
1551 lock
.l_start
= start
;
1553 if (fcntl(fd
, F_SETLK
, &lock
) == -1)