1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    Entrypoint and utility functions
4    Copyright (C) Matthew Chapman 1999-2005
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 2 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 along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
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 */
30 #include <errno.h>
31 #include "rdesktop.h"
32 
33 #ifdef HAVE_LOCALE_H
34 #include <locale.h>
35 #endif
36 #ifdef HAVE_ICONV
37 #ifdef HAVE_LANGINFO_H
38 #include <langinfo.h>
39 #endif
40 #endif
41 
42 #ifdef EGD_SOCKET
43 #include <sys/types.h>
44 #include <sys/socket.h>		/* socket connect */
45 #include <sys/un.h>		/* sockaddr_un */
46 #endif
47 
48 #include <openssl/md5.h>
49 
50 #ifdef RDP2VNC
51 void
52 rdp2vnc_connect(char *server, uint32 flags, char *domain, char *password,
53 		char *shell, char *directory);
54 #endif
55 /* Display usage information */
56 static void
usage(char * program)57 usage(char *program)
58 {
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");
62 
63 	fprintf(stderr, "Usage: %s [options] server[:port]\n", program);
64 #ifdef RDP2VNC
65 	fprintf(stderr, "   -V: vnc port\n");
66 	fprintf(stderr, "   -Q: defer time (ms)\n");
67 #endif
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");
78 #ifdef HAVE_ICONV
79 	fprintf(stderr, "   -L: local codepage\n");
80 #endif
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");
98 	fprintf(stderr,
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");
101 	fprintf(stderr,
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");
106 	fprintf(stderr,
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");
110 	fprintf(stderr,
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");
114 	fprintf(stderr,
115 		"         '-r clipboard:[off|PRIMARYCLIPBOARD|CLIPBOARD]': enable clipboard\n");
116 	fprintf(stderr, "                      redirection.\n");
117 	fprintf(stderr,
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");
124 }
125 
126 static void
print_disconnect_reason(uint16 reason)127 print_disconnect_reason(uint16 reason)
128 {
129 	char *text;
130 
131 	switch (reason)
132 	{
133 		case exDiscReasonNoInfo:
134 			text = "No information available";
135 			break;
136 
137 		case exDiscReasonAPIInitiatedDisconnect:
138 			text = "Server initiated disconnect";
139 			break;
140 
141 		case exDiscReasonAPIInitiatedLogoff:
142 			text = "Server initiated logoff";
143 			break;
144 
145 		case exDiscReasonServerIdleTimeout:
146 			text = "Server idle timeout reached";
147 			break;
148 
149 		case exDiscReasonServerLogonTimeout:
150 			text = "Server logon timeout reached";
151 			break;
152 
153 		case exDiscReasonReplacedByOtherConnection:
154 			text = "The session was replaced";
155 			break;
156 
157 		case exDiscReasonOutOfMemory:
158 			text = "The server is out of memory";
159 			break;
160 
161 		case exDiscReasonServerDeniedConnection:
162 			text = "The server denied the connection";
163 			break;
164 
165 		case exDiscReasonServerDeniedConnectionFips:
166 			text = "The server denied the connection for security reason";
167 			break;
168 
169 		case exDiscReasonLicenseInternal:
170 			text = "Internal licensing error";
171 			break;
172 
173 		case exDiscReasonLicenseNoLicenseServer:
174 			text = "No license server available";
175 			break;
176 
177 		case exDiscReasonLicenseNoLicense:
178 			text = "No valid license available";
179 			break;
180 
181 		case exDiscReasonLicenseErrClientMsg:
182 			text = "Invalid licensing message";
183 			break;
184 
185 		case exDiscReasonLicenseHwidDoesntMatchLicense:
186 			text = "Hardware id doesn't match software license";
187 			break;
188 
189 		case exDiscReasonLicenseErrClientLicense:
190 			text = "Client license error";
191 			break;
192 
193 		case exDiscReasonLicenseCantFinishProtocol:
194 			text = "Network error during licensing protocol";
195 			break;
196 
197 		case exDiscReasonLicenseClientEndedProtocol:
198 			text = "Licensing protocol was not completed";
199 			break;
200 
201 		case exDiscReasonLicenseErrClientEncryption:
202 			text = "Incorrect client license enryption";
203 			break;
204 
205 		case exDiscReasonLicenseCantUpgradeLicense:
206 			text = "Can't upgrade license";
207 			break;
208 
209 		case exDiscReasonLicenseNoRemoteConnections:
210 			text = "The server is not licensed to accept remote connections";
211 			break;
212 
213 		default:
214 			if (reason > 0x1000 && reason < 0x7fff)
215 			{
216 				text = "Internal protocol error";
217 			}
218 			else
219 			{
220 				text = "Unknown reason";
221 			}
222 	}
223 	fprintf(stderr, "disconnect: %s.\n", text);
224 }
225 
226 static void
rdesktop_reset_state(RDPCLIENT * This)227 rdesktop_reset_state(RDPCLIENT * This)
228 {
229 	rdp_reset_state(This);
230 }
231 
232 static BOOL
read_password(char * password,int size)233 read_password(char *password, int size)
234 {
235 	struct termios tios;
236 	BOOL ret = False;
237 	int istty = 0;
238 	char *p;
239 
240 	if (tcgetattr(STDIN_FILENO, &tios) == 0)
241 	{
242 		fprintf(stderr, "Password: ");
243 		tios.c_lflag &= ~ECHO;
244 		tcsetattr(STDIN_FILENO, TCSANOW, &tios);
245 		istty = 1;
246 	}
247 
248 	if (fgets(password, size, stdin) != NULL)
249 	{
250 		ret = True;
251 
252 		/* strip final newline */
253 		p = strchr(password, '\n');
254 		if (p != NULL)
255 			*p = 0;
256 	}
257 
258 	if (istty)
259 	{
260 		tios.c_lflag |= ECHO;
261 		tcsetattr(STDIN_FILENO, TCSANOW, &tios);
262 		fprintf(stderr, "\n");
263 	}
264 
265 	return ret;
266 }
267 
268 static void
parse_server_and_port(RDPCLIENT * This,char * server)269 parse_server_and_port(RDPCLIENT * This, char *server)
270 {
271 	char *p;
272 #ifdef IPv6
273 	int addr_colons;
274 #endif
275 
276 #ifdef IPv6
277 	p = server;
278 	addr_colons = 0;
279 	while (*p)
280 		if (*p++ == ':')
281 			addr_colons++;
282 	if (addr_colons >= 2)
283 	{
284 		/* numeric IPv6 style address format - [1:2:3::4]:port */
285 		p = strchr(server, ']');
286 		if (*server == '[' && p != NULL)
287 		{
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 */
291 			*p = '\0';
292 			strncpy(server, server + 1, strlen(server));
293 		}
294 	}
295 	else
296 	{
297 		/* dns name or IPv4 style address format - server.example.com:port or 1.2.3.4:port */
298 		p = strchr(server, ':');
299 		if (p != NULL)
300 		{
301 			This->tcp_port_rdp = strtol(p + 1, NULL, 10);
302 			*p = 0;
303 		}
304 	}
305 #else /* no IPv6 support */
306 	p = strchr(server, ':');
307 	if (p != NULL)
308 	{
309 		This->tcp_port_rdp = strtol(p + 1, NULL, 10);
310 		*p = 0;
311 	}
312 #endif /* IPv6 */
313 
314 }
315 
316 /* Client program */
317 int
main(int argc,char * argv[])318 main(int argc, char *argv[])
319 {
320 	char server[64];
321 	char fullhostname[64];
322 	char domain[16];
323 	char password[64];
324 	char shell[256];
325 	char directory[256];
326 	BOOL prompt_password, deactivated;
327 	struct passwd *pw;
328 	uint32 flags, ext_disc_reason = 0;
329 	char *p;
330 	int c;
331 	char *locale = NULL;
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 */
336 	RDPCLIENT * This;
337 
338 	This = xmalloc(sizeof(RDPCLIENT));
339 	memset(This, 0, sizeof(RDPCLIENT));
340 
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. */
350 	This->height = 600;
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;
374 
375 #define NOT_SET -1
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;
382 
383 #ifdef HAVE_ICONV
384 	This->rdp.iconv_works = True;
385 #endif
386 
387 	This->xclip.auto_mode = True;
388 
389 #ifdef HAVE_LOCALE_H
390 	/* Set locale according to environment */
391 	locale = setlocale(LC_ALL, "");
392 	if (locale)
393 	{
394 		locale = xstrdup(locale);
395 	}
396 
397 #endif
398 	flags = RDP_LOGON_NORMAL;
399 	prompt_password = False;
400 	domain[0] = password[0] = shell[0] = directory[0] = 0;
401 	This->embed_wnd = 0;
402 
403 	This->num_devices = 0;
404 
405 #ifdef RDP2VNC
406 #define VNCOPT "V:Q:"
407 #else
408 #define VNCOPT
409 #endif
410 
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)
413 	{
414 		switch (c)
415 		{
416 #ifdef RDP2VNC
417 			case 'V':
418 				This->rfb_port = strtol(optarg, NULL, 10);
419 				if (This->rfb_port < 100)
420 					This->rfb_port += 5900;
421 				break;
422 
423 			case 'Q':
424 				This->defer_time = strtol(optarg, NULL, 10);
425 				if (This->defer_time < 0)
426 					This->defer_time = 0;
427 				break;
428 #endif
429 
430 			case 'A':
431 				This->seamless_rdp = True;
432 				break;
433 
434 			case 'u':
435 				STRNCPY(This->username, optarg, sizeof(This->username));
436 				username_option = 1;
437 				break;
438 
439 			case 'L':
440 #ifdef HAVE_ICONV
441 				STRNCPY(This->codepage, optarg, sizeof(This->codepage));
442 #else
443 				error("iconv support not available\n");
444 #endif
445 				break;
446 
447 			case 'd':
448 				STRNCPY(domain, optarg, sizeof(domain));
449 				break;
450 
451 			case 's':
452 				STRNCPY(shell, optarg, sizeof(shell));
453 				break;
454 
455 			case 'c':
456 				STRNCPY(directory, optarg, sizeof(directory));
457 				break;
458 
459 			case 'p':
460 				if ((optarg[0] == '-') && (optarg[1] == 0))
461 				{
462 					prompt_password = True;
463 					break;
464 				}
465 
466 				STRNCPY(password, optarg, sizeof(password));
467 				flags |= RDP_LOGON_AUTO;
468 
469 				/* try to overwrite argument so it won't appear in ps */
470 				p = optarg;
471 				while (*p)
472 					*(p++) = 'X';
473 				break;
474 
475 			case 'n':
476 				STRNCPY(This->hostname, optarg, sizeof(This->hostname));
477 				break;
478 
479 			case 'k':
480 				STRNCPY(This->keymapname, optarg, sizeof(This->keymapname));
481 				break;
482 
483 			case 'g':
484 				geometry_option = True;
485 				This->fullscreen = False;
486 				if (!strcmp(optarg, "workarea"))
487 				{
488 					This->width = This->height = 0;
489 					break;
490 				}
491 
492 				This->width = strtol(optarg, &p, 10);
493 				if (This->width <= 0)
494 				{
495 					error("invalid geometry\n");
496 					return 1;
497 				}
498 
499 				if (*p == 'x')
500 					This->height = strtol(p + 1, &p, 10);
501 
502 				if (This->height <= 0)
503 				{
504 					error("invalid geometry\n");
505 					return 1;
506 				}
507 
508 				if (*p == '%')
509 				{
510 					This->width = -This->width;
511 					p++;
512 				}
513 
514 				if (*p == '+' || *p == '-')
515 				{
516 					This->pos |= (*p == '-') ? 2 : 1;
517 					This->xpos = strtol(p, &p, 10);
518 
519 				}
520 				if (*p == '+' || *p == '-')
521 				{
522 					This->pos |= (*p == '-') ? 4 : 1;
523 					This->ypos = strtol(p, NULL, 10);
524 				}
525 
526 				break;
527 
528 			case 'f':
529 				This->fullscreen = True;
530 				break;
531 
532 			case 'b':
533 				This->bitmap_cache = False;
534 				break;
535 
536 			case 'B':
537 				This->ownbackstore = False;
538 				break;
539 
540 			case 'e':
541 				This->encryption = False;
542 				break;
543 			case 'E':
544 				This->packet_encryption = False;
545 				break;
546 			case 'm':
547 				This->sendmotion = False;
548 				break;
549 
550 			case 'C':
551 				This->owncolmap = True;
552 				break;
553 
554 			case 'D':
555 				This->hide_decorations = True;
556 				break;
557 
558 			case 'K':
559 				This->grab_keyboard = False;
560 				break;
561 
562 			case 'S':
563 				if (!strcmp(optarg, "standard"))
564 				{
565 					This->win_button_size = 18;
566 					break;
567 				}
568 
569 				This->win_button_size = strtol(optarg, &p, 10);
570 
571 				if (*p)
572 				{
573 					error("invalid button size\n");
574 					return 1;
575 				}
576 
577 				break;
578 
579 			case 'T':
580 				STRNCPY(This->title, optarg, sizeof(This->title));
581 				break;
582 
583 			case 'N':
584 				This->numlock_sync = True;
585 				break;
586 
587 			case 'X':
588 				This->embed_wnd = strtol(optarg, NULL, 0);
589 				break;
590 
591 			case 'a':
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)
596 				{
597 					error("Invalid server colour depth.\n");
598 					return 1;
599 				}
600 				break;
601 
602 			case 'z':
603 				DEBUG(("rdp compression enabled\n"));
604 				flags |= (RDP_LOGON_COMPRESSION | RDP_LOGON_COMPRESSION2);
605 				break;
606 
607 			case 'x':
608 				if (str_startswith(optarg, "m"))	/* modem */
609 				{
610 					This->rdp5_performanceflags =
611 						RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG |
612 						RDP5_NO_MENUANIMATIONS | RDP5_NO_THEMING;
613 				}
614 				else if (str_startswith(optarg, "b"))	/* broadband */
615 				{
616 					This->rdp5_performanceflags = RDP5_NO_WALLPAPER;
617 				}
618 				else if (str_startswith(optarg, "l"))	/* lan */
619 				{
620 					This->rdp5_performanceflags = RDP5_DISABLE_NOTHING;
621 				}
622 				else
623 				{
624 					This->rdp5_performanceflags = strtol(optarg, NULL, 16);
625 				}
626 				break;
627 
628 			case 'P':
629 				This->bitmap_cache_persist_enable = True;
630 				break;
631 
632 			case 'r':
633 
634 				if (str_startswith(optarg, "sound"))
635 				{
636 					optarg += 5;
637 
638 					if (*optarg == ':')
639 					{
640 						optarg++;
641 						while ((p = next_arg(optarg, ',')))
642 						{
643 							if (str_startswith(optarg, "remote"))
644 								flags |= RDP_LOGON_LEAVE_AUDIO;
645 
646 							if (str_startswith(optarg, "local"))
647 #ifdef WITH_RDPSND
648 								This->rdpsnd_enabled = True;
649 #else
650 								warning("Not compiled with sound support\n");
651 #endif
652 
653 							if (str_startswith(optarg, "off"))
654 #ifdef WITH_RDPSND
655 								This->rdpsnd_enabled = False;
656 #else
657 								warning("Not compiled with sound support\n");
658 #endif
659 
660 							optarg = p;
661 						}
662 					}
663 					else
664 					{
665 #ifdef WITH_RDPSND
666 						This->rdpsnd_enabled = True;
667 #else
668 						warning("Not compiled with sound support\n");
669 #endif
670 					}
671 				}
672 				else if (str_startswith(optarg, "disk"))
673 				{
674 					/* -r disk:h:=/mnt/floppy */
675 					disk_enum_devices(This, &This->num_devices, optarg + 4);
676 				}
677 				else if (str_startswith(optarg, "comport"))
678 				{
679 					serial_enum_devices(This, &This->num_devices, optarg + 7);
680 				}
681 				else if (str_startswith(optarg, "lspci"))
682 				{
683 					This->lspci_enabled = True;
684 				}
685 				else if (str_startswith(optarg, "lptport"))
686 				{
687 					parallel_enum_devices(This, &This->num_devices, optarg + 7);
688 				}
689 				else if (str_startswith(optarg, "printer"))
690 				{
691 					printer_enum_devices(This, &This->num_devices, optarg + 7);
692 				}
693 				else if (str_startswith(optarg, "clientname"))
694 				{
695 					This->rdpdr_clientname = xmalloc(strlen(optarg + 11) + 1);
696 					strcpy(This->rdpdr_clientname, optarg + 11);
697 				}
698 				else if (str_startswith(optarg, "clipboard"))
699 				{
700 					optarg += 9;
701 
702 					if (*optarg == ':')
703 					{
704 						optarg++;
705 
706 						if (str_startswith(optarg, "off"))
707 							This->rdpclip = False;
708 						else
709 							cliprdr_set_mode(This, optarg);
710 					}
711 					else
712 						This->rdpclip = True;
713 				}
714 				else
715 				{
716 					warning("Unknown -r argument\n\n\tPossible arguments are: comport, disk, lptport, printer, sound, clipboard\n");
717 				}
718 				break;
719 
720 			case '0':
721 				This->console_session = True;
722 				break;
723 
724 			case '4':
725 				This->use_rdp5 = False;
726 				break;
727 
728 			case '5':
729 				This->use_rdp5 = True;
730 				break;
731 
732 			case 'h':
733 			case '?':
734 			default:
735 				usage(argv[0]);
736 				return 1;
737 		}
738 	}
739 
740 	if (argc - optind != 1)
741 	{
742 		usage(argv[0]);
743 		return 1;
744 	}
745 
746 	STRNCPY(server, argv[optind], sizeof(server));
747 	parse_server_and_port(This, server);
748 
749 	if (This->seamless_rdp)
750 	{
751 		if (This->win_button_size)
752 		{
753 			error("You cannot use -S and -A at the same time\n");
754 			return 1;
755 		}
756 		This->rdp5_performanceflags &= ~RDP5_NO_FULLWINDOWDRAG;
757 		if (geometry_option)
758 		{
759 			error("You cannot use -g and -A at the same time\n");
760 			return 1;
761 		}
762 		if (This->fullscreen)
763 		{
764 			error("You cannot use -f and -A at the same time\n");
765 			return 1;
766 		}
767 		if (This->hide_decorations)
768 		{
769 			error("You cannot use -D and -A at the same time\n");
770 			return 1;
771 		}
772 		if (This->embed_wnd)
773 		{
774 			error("You cannot use -X and -A at the same time\n");
775 			return 1;
776 		}
777 		if (!This->use_rdp5)
778 		{
779 			error("You cannot use -4 and -A at the same time\n");
780 			return 1;
781 		}
782 		This->width = -100;
783 		This->grab_keyboard = False;
784 	}
785 
786 	if (!username_option)
787 	{
788 		pw = getpwuid(getuid());
789 		if ((pw == NULL) || (pw->pw_name == NULL))
790 		{
791 			error("could not determine username, use -u\n");
792 			return 1;
793 		}
794 
795 		STRNCPY(This->username, pw->pw_name, sizeof(This->username));
796 	}
797 
798 #ifdef HAVE_ICONV
799 	if (This->codepage[0] == 0)
800 	{
801 		if (setlocale(LC_CTYPE, ""))
802 		{
803 			STRNCPY(This->codepage, nl_langinfo(CODESET), sizeof(This->codepage));
804 		}
805 		else
806 		{
807 			STRNCPY(This->codepage, DEFAULT_CODEPAGE, sizeof(This->codepage));
808 		}
809 	}
810 #endif
811 
812 	if (This->hostname[0] == 0)
813 	{
814 		if (gethostname(fullhostname, sizeof(fullhostname)) == -1)
815 		{
816 			error("could not determine local hostname, use -n\n");
817 			return 1;
818 		}
819 
820 		p = strchr(fullhostname, '.');
821 		if (p != NULL)
822 			*p = 0;
823 
824 		STRNCPY(This->hostname, fullhostname, sizeof(This->hostname));
825 	}
826 
827 	if (This->keymapname[0] == 0)
828 	{
829 		if (locale && xkeymap_from_locale(This, locale))
830 		{
831 			fprintf(stderr, "Autoselected keyboard map %s\n", This->keymapname);
832 		}
833 		else
834 		{
835 			STRNCPY(This->keymapname, "en-us", sizeof(This->keymapname));
836 		}
837 	}
838 	if (locale)
839 		xfree(locale);
840 
841 
842 	if (prompt_password && read_password(password, sizeof(password)))
843 		flags |= RDP_LOGON_AUTO;
844 
845 	if (This->title[0] == 0)
846 	{
847 		strcpy(This->title, "rdesktop - ");
848 		strncat(This->title, server, sizeof(This->title) - sizeof("rdesktop - "));
849 	}
850 
851 #ifdef RDP2VNC
852 	rdp2vnc_connect(server, flags, domain, password, shell, directory);
853 	return 0;
854 #else
855 
856 	if (!ui_init(This))
857 		return 1;
858 
859 #ifdef WITH_RDPSND
860 	if (This->rdpsnd_enabled)
861 		rdpsnd_init(This);
862 #endif
863 
864 	if (This->lspci_enabled)
865 		lspci_init(This);
866 
867 	rdpdr_init(This);
868 
869 	while (run_count < 2 && continue_connect)	/* add support for Session Directory; only reconnect once */
870 	{
871 		if (run_count == 0)
872 		{
873 			if (!rdp_connect(This, server, flags, domain, password, shell, directory))
874 				return 1;
875 		}
876 		else if (!rdp_reconnect
877 			 (This, server, flags, domain, password, shell, directory, This->redirect_cookie))
878 			return 1;
879 
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;
884 
885 
886 		DEBUG(("Connection successful.\n"));
887 		memset(password, 0, sizeof(password));
888 
889 		if (run_count == 0)
890 			if (!ui_create_window(This))
891 				continue_connect = False;
892 
893 		if (continue_connect)
894 			rdp_main_loop(This, &deactivated, &ext_disc_reason);
895 
896 		DEBUG(("Disconnecting...\n"));
897 		rdp_disconnect(This);
898 
899 		if ((This->redirect == True) && (run_count == 0))	/* Support for Session Directory */
900 		{
901 			/* reset state of major globals */
902 			rdesktop_reset_state(This);
903 
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;
909 
910 			This->redirect = False;
911 		}
912 		else
913 		{
914 			continue_connect = False;
915 			ui_destroy_window(This);
916 			break;
917 		}
918 
919 		run_count++;
920 	}
921 
922 	cache_save_state(This);
923 	ui_deinit(This);
924 
925 	if (ext_disc_reason >= 2)
926 		print_disconnect_reason(ext_disc_reason);
927 
928 	if (deactivated)
929 	{
930 		/* clean disconnect */
931 		return 0;
932 	}
933 	else
934 	{
935 		if (ext_disc_reason == exDiscReasonAPIInitiatedDisconnect
936 		    || ext_disc_reason == exDiscReasonAPIInitiatedLogoff)
937 		{
938 			/* not so clean disconnect, but nothing to worry about */
939 			return 0;
940 		}
941 		else
942 		{
943 			/* return error */
944 			return 2;
945 		}
946 	}
947 
948 #endif
949 
950 }
951 
952 #ifdef EGD_SOCKET
953 /* Read 32 random bytes from PRNGD or EGD socket (based on OpenSSL RAND_egd) */
954 static BOOL
generate_random_egd(uint8 * buf)955 generate_random_egd(uint8 * buf)
956 {
957 	struct sockaddr_un addr;
958 	BOOL ret = False;
959 	int fd;
960 
961 	fd = socket(AF_UNIX, SOCK_STREAM, 0);
962 	if (fd == -1)
963 		return False;
964 
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)
968 		goto err;
969 
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)
974 		goto err;
975 
976 	if ((read(fd, buf, 1) != 1) || (buf[0] == 0))	/* Available? */
977 		goto err;
978 
979 	if (read(fd, buf, 32) != 32)
980 		goto err;
981 
982 	ret = True;
983 
984       err:
985 	close(fd);
986 	return ret;
987 }
988 #endif
989 
990 /* Generate a 32-byte random for the secure transport code. */
991 void
generate_random(uint8 * random)992 generate_random(uint8 * random)
993 {
994 	struct stat st;
995 	struct tms tmsbuf;
996 	MD5_CTX md5;
997 	uint32 *r;
998 	int fd, n;
999 
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))
1003 	{
1004 		n = read(fd, random, 32);
1005 		close(fd);
1006 		if (n == 32)
1007 			return;
1008 	}
1009 
1010 #ifdef EGD_SOCKET
1011 	/* As a second preference use an EGD */
1012 	if (generate_random_egd(random))
1013 		return;
1014 #endif
1015 
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 */
1022 	stat("/tmp", &st);
1023 	r[5] = st.st_atime;
1024 	r[6] = st.st_mtime;
1025 	r[7] = st.st_ctime;
1026 
1027 	/* Hash both halves with MD5 to obscure possible patterns */
1028 	MD5_Init(&md5);
1029 	MD5_Update(&md5, random, 16);
1030 	MD5_Final(random, &md5);
1031 	MD5_Update(&md5, random + 16, 16);
1032 	MD5_Final(random + 16, &md5);
1033 }
1034 
1035 /* malloc; exit if out of memory */
1036 void *
xmalloc(int size)1037 xmalloc(int size)
1038 {
1039 	void *mem = malloc(size);
1040 	if (mem == NULL)
1041 	{
1042 		error("xmalloc %d\n", size);
1043 		exit(1);
1044 	}
1045 	return mem;
1046 }
1047 
1048 /* strdup */
1049 char *
xstrdup(const char * s)1050 xstrdup(const char *s)
1051 {
1052 	char *mem = strdup(s);
1053 	if (mem == NULL)
1054 	{
1055 		perror("strdup");
1056 		exit(1);
1057 	}
1058 	return mem;
1059 }
1060 
1061 /* realloc; exit if out of memory */
1062 void *
xrealloc(void * oldmem,int size)1063 xrealloc(void *oldmem, int size)
1064 {
1065 	void *mem;
1066 
1067 	if (size < 1)
1068 		size = 1;
1069 	mem = realloc(oldmem, size);
1070 	if (mem == NULL)
1071 	{
1072 		error("xrealloc %d\n", size);
1073 		exit(1);
1074 	}
1075 	return mem;
1076 }
1077 
1078 /* free */
1079 void
xfree(void * mem)1080 xfree(void *mem)
1081 {
1082 	free(mem);
1083 }
1084 
1085 /* report an error */
1086 void
error(char * format,...)1087 error(char *format, ...)
1088 {
1089 	va_list ap;
1090 
1091 	fprintf(stderr, "ERROR: ");
1092 
1093 	va_start(ap, format);
1094 	vfprintf(stderr, format, ap);
1095 	va_end(ap);
1096 }
1097 
1098 /* report a warning */
1099 void
warning(char * format,...)1100 warning(char *format, ...)
1101 {
1102 	va_list ap;
1103 
1104 	fprintf(stderr, "WARNING: ");
1105 
1106 	va_start(ap, format);
1107 	vfprintf(stderr, format, ap);
1108 	va_end(ap);
1109 }
1110 
1111 /* report an unimplemented protocol feature */
1112 void
unimpl(char * format,...)1113 unimpl(char *format, ...)
1114 {
1115 	va_list ap;
1116 
1117 	fprintf(stderr, "NOT IMPLEMENTED: ");
1118 
1119 	va_start(ap, format);
1120 	vfprintf(stderr, format, ap);
1121 	va_end(ap);
1122 }
1123 
1124 /* produce a hex dump */
1125 void
hexdump(unsigned char * p,unsigned int len)1126 hexdump(unsigned char *p, unsigned int len)
1127 {
1128 	unsigned char *line = p;
1129 	int i, thisline, offset = 0;
1130 
1131 	while (offset < len)
1132 	{
1133 		printf("%04x ", offset);
1134 		thisline = len - offset;
1135 		if (thisline > 16)
1136 			thisline = 16;
1137 
1138 		for (i = 0; i < thisline; i++)
1139 			printf("%02x ", line[i]);
1140 
1141 		for (; i < 16; i++)
1142 			printf("   ");
1143 
1144 		for (i = 0; i < thisline; i++)
1145 			printf("%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.');
1146 
1147 		printf("\n");
1148 		offset += thisline;
1149 		line += thisline;
1150 	}
1151 }
1152 
1153 /*
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.
1160   pitfalls:
1161   	src is modified. 0x00 chars are inserted to
1162 	terminate strings.
1163 	return val, points on the next val chr after ins
1164 	0x00
1165 
1166 	example usage:
1167 	while( (pos = next_arg( optarg, ',')) ){
1168 		printf("%s\n",optarg);
1169 		optarg=pos;
1170 	}
1171 
1172 */
1173 char *
next_arg(char * src,char needle)1174 next_arg(char *src, char needle)
1175 {
1176 	char *nextval;
1177 	char *p;
1178 	char *mvp = 0;
1179 
1180 	/* EOS */
1181 	if (*src == (char) 0x00)
1182 		return 0;
1183 
1184 	p = src;
1185 	/*  skip escaped needles */
1186 	while ((nextval = strchr(p, needle)))
1187 	{
1188 		mvp = nextval - 1;
1189 		/* found backslashed needle */
1190 		if (*mvp == '\\' && (mvp > src))
1191 		{
1192 			/* move string one to the left */
1193 			while (*(mvp + 1) != (char) 0x00)
1194 			{
1195 				*mvp = *(mvp + 1);
1196 				mvp++;
1197 			}
1198 			*mvp = (char) 0x00;
1199 			p = nextval;
1200 		}
1201 		else
1202 		{
1203 			p = nextval + 1;
1204 			break;
1205 		}
1206 
1207 	}
1208 
1209 	/* more args available */
1210 	if (nextval)
1211 	{
1212 		*nextval = (char) 0x00;
1213 		return ++nextval;
1214 	}
1215 
1216 	/* no more args after this, jump to EOS */
1217 	nextval = src + strlen(src);
1218 	return nextval;
1219 }
1220 
1221 
1222 void
toupper_str(char * p)1223 toupper_str(char *p)
1224 {
1225 	while (*p)
1226 	{
1227 		if ((*p >= 'a') && (*p <= 'z'))
1228 			*p = toupper((int) *p);
1229 		p++;
1230 	}
1231 }
1232 
1233 
1234 BOOL
str_startswith(const char * s,const char * prefix)1235 str_startswith(const char *s, const char *prefix)
1236 {
1237 	return (strncmp(s, prefix, strlen(prefix)) == 0);
1238 }
1239 
1240 
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.  */
1245 BOOL
str_handle_lines(RDPCLIENT * This,const char * input,char ** rest,str_handle_lines_t linehandler,void * data)1246 str_handle_lines(RDPCLIENT * This, const char *input, char **rest, str_handle_lines_t linehandler, void *data)
1247 {
1248 	char *buf, *p;
1249 	char *oldrest;
1250 	size_t inputlen;
1251 	size_t buflen;
1252 	size_t restlen = 0;
1253 	BOOL ret = True;
1254 
1255 	/* Copy data to buffer */
1256 	inputlen = strlen(input);
1257 	if (*rest)
1258 		restlen = strlen(*rest);
1259 	buflen = restlen + inputlen + 1;
1260 	buf = (char *) xmalloc(buflen);
1261 	buf[0] = '\0';
1262 	if (*rest)
1263 		STRNCPY(buf, *rest, buflen);
1264 	strncat(buf, input, inputlen);
1265 	p = buf;
1266 
1267 	while (1)
1268 	{
1269 		char *newline = strchr(p, '\n');
1270 		if (newline)
1271 		{
1272 			*newline = '\0';
1273 			if (!linehandler(This, p, data))
1274 			{
1275 				p = newline + 1;
1276 				ret = False;
1277 				break;
1278 			}
1279 			p = newline + 1;
1280 		}
1281 		else
1282 		{
1283 			break;
1284 
1285 		}
1286 	}
1287 
1288 	/* Save in rest */
1289 	oldrest = *rest;
1290 	restlen = buf + buflen - p;
1291 	*rest = (char *) xmalloc(restlen);
1292 	STRNCPY((*rest), p, restlen);
1293 	xfree(oldrest);
1294 
1295 	xfree(buf);
1296 	return ret;
1297 }
1298 
1299 /* Execute the program specified by argv. For each line in
1300    stdout/stderr output, call linehandler. Returns false on failure. */
1301 BOOL
subprocess(RDPCLIENT * This,char * const argv[],str_handle_lines_t linehandler,void * data)1302 subprocess(RDPCLIENT * This, char *const argv[], str_handle_lines_t linehandler, void *data)
1303 {
1304 	pid_t child;
1305 	int fd[2];
1306 	int n = 1;
1307 	char output[256];
1308 	char *rest = NULL;
1309 
1310 	if (pipe(fd) < 0)
1311 	{
1312 		perror("pipe");
1313 		return False;
1314 	}
1315 
1316 	if ((child = fork()) < 0)
1317 	{
1318 		perror("fork");
1319 		return False;
1320 	}
1321 
1322 	/* Child */
1323 	if (child == 0)
1324 	{
1325 		/* Close read end */
1326 		close(fd[0]);
1327 
1328 		/* Redirect stdout and stderr to pipe */
1329 		dup2(fd[1], 1);
1330 		dup2(fd[1], 2);
1331 
1332 		/* Execute */
1333 		execvp(argv[0], argv);
1334 		perror("Error executing child");
1335 		_exit(128);
1336 	}
1337 
1338 	/* Parent. Close write end. */
1339 	close(fd[1]);
1340 	while (n > 0)
1341 	{
1342 		n = read(fd[0], output, 255);
1343 		output[n] = '\0';
1344 		str_handle_lines(This, output, &rest, linehandler, data);
1345 	}
1346 	xfree(rest);
1347 
1348 	return True;
1349 }
1350 
1351 
1352 /* not all clibs got ltoa */
1353 #define LTOA_BUFSIZE (sizeof(long) * 8 + 1)
1354 
1355 char *
l_to_a(long N,int base)1356 l_to_a(long N, int base)
1357 {
1358 	static char ret[LTOA_BUFSIZE];
1359 
1360 	char *head = ret, buf[LTOA_BUFSIZE], *tail = buf + sizeof(buf);
1361 
1362 	register int divrem;
1363 
1364 	if (base < 36 || 2 > base)
1365 		base = 10;
1366 
1367 	if (N < 0)
1368 	{
1369 		*head++ = '-';
1370 		N = -N;
1371 	}
1372 
1373 	tail = buf + sizeof(buf);
1374 	*--tail = 0;
1375 
1376 	do
1377 	{
1378 		divrem = N % base;
1379 		*--tail = (divrem <= 9) ? divrem + '0' : divrem + 'a' - 10;
1380 		N /= base;
1381 	}
1382 	while (N);
1383 
1384 	strcpy(head, tail);
1385 	return ret;
1386 }
1387 
1388 
1389 int
load_licence(RDPCLIENT * This,unsigned char ** data)1390 load_licence(RDPCLIENT * This, unsigned char **data)
1391 {
1392 	char *home, *path;
1393 	struct stat st;
1394 	int fd, length;
1395 
1396 	home = getenv("HOME");
1397 	if (home == NULL)
1398 		return -1;
1399 
1400 	path = (char *) xmalloc(strlen(home) + strlen(This->hostname) + sizeof("/.rdesktop/licence."));
1401 	sprintf(path, "%s/.rdesktop/licence.%s", home, This->hostname);
1402 
1403 	fd = open(path, O_RDONLY);
1404 	if (fd == -1)
1405 		return -1;
1406 
1407 	if (fstat(fd, &st))
1408 	{
1409 		close(fd);
1410 		return -1;
1411 	}
1412 
1413 	*data = (uint8 *) xmalloc(st.st_size);
1414 	length = read(fd, *data, st.st_size);
1415 	close(fd);
1416 	xfree(path);
1417 	return length;
1418 }
1419 
1420 void
save_licence(RDPCLIENT * This,unsigned char * data,int length)1421 save_licence(RDPCLIENT * This, unsigned char *data, int length)
1422 {
1423 	char *home, *path, *tmppath;
1424 	int fd;
1425 
1426 	home = getenv("HOME");
1427 	if (home == NULL)
1428 		return;
1429 
1430 	path = (char *) xmalloc(strlen(home) + strlen(This->hostname) + sizeof("/.rdesktop/licence."));
1431 
1432 	sprintf(path, "%s/.rdesktop", home);
1433 	if ((mkdir(path, 0700) == -1) && errno != EEXIST)
1434 	{
1435 		perror(path);
1436 		return;
1437 	}
1438 
1439 	/* write licence to licence.hostname.new, then atomically rename to licence.hostname */
1440 
1441 	sprintf(path, "%s/.rdesktop/licence.%s", home, This->hostname);
1442 	tmppath = (char *) xmalloc(strlen(path) + sizeof(".new"));
1443 	strcpy(tmppath, path);
1444 	strcat(tmppath, ".new");
1445 
1446 	fd = open(tmppath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1447 	if (fd == -1)
1448 	{
1449 		perror(tmppath);
1450 		return;
1451 	}
1452 
1453 	if (write(fd, data, length) != length)
1454 	{
1455 		perror(tmppath);
1456 		unlink(tmppath);
1457 	}
1458 	else if (rename(tmppath, path) == -1)
1459 	{
1460 		perror(path);
1461 		unlink(tmppath);
1462 	}
1463 
1464 	close(fd);
1465 	xfree(tmppath);
1466 	xfree(path);
1467 }
1468 
1469 /* Create the bitmap cache directory */
1470 BOOL
rd_pstcache_mkdir(void)1471 rd_pstcache_mkdir(void)
1472 {
1473 	char *home;
1474 	char bmpcache_dir[256];
1475 
1476 	home = getenv("HOME");
1477 
1478 	if (home == NULL)
1479 		return False;
1480 
1481 	sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop");
1482 
1483 	if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST)
1484 	{
1485 		perror(bmpcache_dir);
1486 		return False;
1487 	}
1488 
1489 	sprintf(bmpcache_dir, "%s/%s", home, ".rdesktop/cache");
1490 
1491 	if ((mkdir(bmpcache_dir, S_IRWXU) == -1) && errno != EEXIST)
1492 	{
1493 		perror(bmpcache_dir);
1494 		return False;
1495 	}
1496 
1497 	return True;
1498 }
1499 
1500 /* open a file in the .rdesktop directory */
1501 int
rd_open_file(char * filename)1502 rd_open_file(char *filename)
1503 {
1504 	char *home;
1505 	char fn[256];
1506 	int fd;
1507 
1508 	home = getenv("HOME");
1509 	if (home == NULL)
1510 		return -1;
1511 	sprintf(fn, "%s/.rdesktop/%s", home, filename);
1512 	fd = open(fn, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
1513 	if (fd == -1)
1514 		perror(fn);
1515 	return fd;
1516 }
1517 
1518 /* close file */
1519 void
rd_close_file(int fd)1520 rd_close_file(int fd)
1521 {
1522 	close(fd);
1523 }
1524 
1525 /* read from file*/
1526 int
rd_read_file(int fd,void * ptr,int len)1527 rd_read_file(int fd, void *ptr, int len)
1528 {
1529 	return read(fd, ptr, len);
1530 }
1531 
1532 /* write to file */
1533 int
rd_write_file(int fd,void * ptr,int len)1534 rd_write_file(int fd, void *ptr, int len)
1535 {
1536 	return write(fd, ptr, len);
1537 }
1538 
1539 /* move file pointer */
1540 int
rd_lseek_file(int fd,int offset)1541 rd_lseek_file(int fd, int offset)
1542 {
1543 	return lseek(fd, offset, SEEK_SET);
1544 }
1545 
1546 /* do a write lock on a file */
1547 BOOL
rd_lock_file(int fd,int start,int len)1548 rd_lock_file(int fd, int start, int len)
1549 {
1550 	struct flock lock;
1551 
1552 	lock.l_type = F_WRLCK;
1553 	lock.l_whence = SEEK_SET;
1554 	lock.l_start = start;
1555 	lock.l_len = len;
1556 	if (fcntl(fd, F_SETLK, &lock) == -1)
1557 		return False;
1558 	return True;
1559 }
1560