1 /*
2 Copyright (C) 2002-2010 Karl J. Runge <runge@karlrunge.com>
3 All rights reserved.
4
5 This file is part of x11vnc.
6
7 x11vnc is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at
10 your option) any later version.
11
12 x11vnc is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with x11vnc; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
20 or see <http://www.gnu.org/licenses/>.
21
22 In addition, as a special exception, Karl J. Runge
23 gives permission to link the code of its release of x11vnc with the
24 OpenSSL project's "OpenSSL" library (or with modified versions of it
25 that use the same license as the "OpenSSL" library), and distribute
26 the linked executables. You must obey the GNU General Public License
27 in all respects for all of the code used other than "OpenSSL". If you
28 modify this file, you may extend this exception to your version of the
29 file, but you are not obligated to do so. If you do not wish to do
30 so, delete this exception statement from your version.
31 */
32
33 /* -- connections.c -- */
34
35 #include "x11vnc.h"
36 #include "inet.h"
37 #include "remote.h"
38 #include "keyboard.h"
39 #include "cleanup.h"
40 #include "gui.h"
41 #include "solid.h"
42 #include "rates.h"
43 #include "screen.h"
44 #include "unixpw.h"
45 #include "user.h"
46 #include "scan.h"
47 #include "sslcmds.h"
48 #include "sslhelper.h"
49 #include "xwrappers.h"
50 #include "xevents.h"
51 #include "win_utils.h"
52 #include "macosx.h"
53 #include "macosxCG.h"
54 #include "userinput.h"
55 #include "pointer.h"
56 #include "xrandr.h"
57 #include "xi2_devices.h"
58
59
60 /*
61 * routines for handling incoming, outgoing, etc connections
62 */
63
64 /* string for the VNC_CONNECT property */
65 char vnc_connect_str[VNC_CONNECT_MAX+1];
66 Atom vnc_connect_prop = None;
67 char x11vnc_remote_str[X11VNC_REMOTE_MAX+1];
68 Atom x11vnc_remote_prop = None;
69 rfbClientPtr inetd_client = NULL;
70
71 int all_clients_initialized(void);
72 char *list_clients(void);
73 int new_fb_size_clients(rfbScreenInfoPtr s);
74 void close_all_clients(void);
75 void close_clients(char *str);
76 void set_client_input(char *str);
77 void set_child_info(void);
78 int cmd_ok(char *cmd);
79 void client_gone(rfbClientPtr client);
80 void client_gone_chat_helper(rfbClientPtr client);
81 void reverse_connect(char *str);
82 void set_vnc_connect_prop(char *str);
83 void read_vnc_connect_prop(int);
84 void set_x11vnc_remote_prop(char *str);
85 void read_x11vnc_remote_prop(int);
86 void check_connect_inputs(void);
87 void check_gui_inputs(void);
88 rfbClientPtr create_new_client(int sock, int start_thread);
89 enum rfbNewClientAction new_client(rfbClientPtr client);
90 enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client);
91 rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len);
92 void start_client_info_sock(char *host_port_cookie);
93 void send_client_info(char *str);
94 void adjust_grabs(int grab, int quiet);
95 void check_new_clients(void);
96 int accept_client(rfbClientPtr client);
97 void check_ipv6_listen(long usec);
98 void check_unix_sock(long usec);
99 int run_user_command(char *cmd, rfbClientPtr client, char *mode, char *input,
100 int len, FILE *output);
101 int check_access(char *addr);
102 void client_set_net(rfbClientPtr client);
103 char *get_xprop(char *prop, Window win);
104 int set_xprop(char *prop, Window win, char *value);
105 char *bcx_xattach(char *str, int *pg_init, int *kg_init);
106 void grab_state(int *ptr_grabbed, int *kbd_grabbed);
107 char *wininfo(Window win, int show_children);
108
109 static rfbClientPtr *client_match(char *str);
110 static void free_client_data(rfbClientPtr client);
111 static void ugly_geom(char *p, int *x, int *y);
112 static int ugly_window(char *addr, char *userhost, int X, int Y,
113 int timeout, char *mode, int accept);
114 static int action_match(char *action, int rc);
115 static void check_connect_file(char *file);
116 static void send_client_connect(void);
117
118
119 /*
120 * check that all clients are in RFB_NORMAL state
121 */
all_clients_initialized(void)122 int all_clients_initialized(void) {
123 rfbClientIteratorPtr iter;
124 rfbClientPtr cl;
125 int ok = 1;
126
127 if (! screen) {
128 return ok;
129 }
130
131 iter = rfbGetClientIterator(screen);
132 while( (cl = rfbClientIteratorNext(iter)) ) {
133 if (cl->state != RFB_NORMAL) {
134 ok = 0;
135 } else {
136 client_normal_count++;
137 }
138 }
139 rfbReleaseClientIterator(iter);
140
141 return ok;
142 }
143
list_clients(void)144 char *list_clients(void) {
145 rfbClientIteratorPtr iter;
146 rfbClientPtr cl;
147 char *list, tmp[256];
148 int count = 0;
149
150 if (!screen) {
151 return strdup("");
152 }
153
154 iter = rfbGetClientIterator(screen);
155 while( (cl = rfbClientIteratorNext(iter)) ) {
156 client_set_net(cl);
157 count++;
158 }
159 rfbReleaseClientIterator(iter);
160
161 /*
162 * each client:
163 * <id>:<ip>:<port>:<user>:<unix>:<hostname>:<input>:<loginview>:<time>,
164 * 8+1+64+1+5+1+24+1+24+1+256+1+5+1+1+1+10+1
165 * 123.123.123.123:60000/0x11111111-rw,
166 * so count+1 * 1000 must cover it.
167 */
168 list = (char *) malloc((count+1)*1000);
169
170 list[0] = '\0';
171
172 iter = rfbGetClientIterator(screen);
173 while( (cl = rfbClientIteratorNext(iter)) ) {
174 ClientData *cd = (ClientData *) cl->clientData;
175 char *tmp_host, *p;
176
177 if (! cd) {
178 continue;
179 }
180 if (*list != '\0') {
181 strcat(list, ",");
182 }
183 sprintf(tmp, "0x%x:", cd->uid);
184 strcat(list, tmp);
185 p = tmp_host = strdup(cl->host);
186 while (*p) {
187 if (*p == ':') *p = '#';
188 p++;
189 }
190 strcat(list, tmp_host);
191 free(tmp_host);
192 strcat(list, ":");
193 sprintf(tmp, "%d:", cd->client_port);
194 strcat(list, tmp);
195 if (cd->username[0] == '\0') {
196 char *s = ident_username(cl);
197 if (s) free(s);
198 }
199 if (strstr(cd->username, "UNIX:") == cd->username) {
200 strcat(list, cd->username + strlen("UNIX:"));
201 } else {
202 strcat(list, cd->username);
203 }
204 strcat(list, ":");
205 if (cd->unixname[0] == '\0') {
206 strcat(list, "none");
207 } else {
208 strcat(list, cd->unixname);
209 }
210 strcat(list, ":");
211 p = tmp_host = strdup(cd->hostname);
212 while (*p) {
213 if (*p == ':') *p = '#';
214 p++;
215 }
216 strcat(list, tmp_host);
217 free(tmp_host);
218 strcat(list, ":");
219 strcat(list, cd->input);
220 strcat(list, ":");
221 sprintf(tmp, "%d", cd->login_viewonly);
222 strcat(list, tmp);
223 strcat(list, ":");
224 sprintf(tmp, "%d", (int) cd->login_time);
225 strcat(list, tmp);
226 }
227 rfbReleaseClientIterator(iter);
228 return list;
229 }
230
231 /* count number of clients supporting NewFBSize */
new_fb_size_clients(rfbScreenInfoPtr s)232 int new_fb_size_clients(rfbScreenInfoPtr s) {
233 rfbClientIteratorPtr iter;
234 rfbClientPtr cl;
235 int count = 0;
236
237 if (! s) {
238 return 0;
239 }
240
241 iter = rfbGetClientIterator(s);
242 while( (cl = rfbClientIteratorNext(iter)) ) {
243 if (cl->useNewFBSize) {
244 count++;
245 }
246 }
247 rfbReleaseClientIterator(iter);
248 return count;
249 }
250
close_all_clients(void)251 void close_all_clients(void) {
252 rfbClientIteratorPtr iter;
253 rfbClientPtr cl;
254
255 if (! screen) {
256 return;
257 }
258
259 iter = rfbGetClientIterator(screen);
260 while( (cl = rfbClientIteratorNext(iter)) ) {
261 rfbCloseClient(cl);
262 rfbClientConnectionGone(cl);
263 }
264 rfbReleaseClientIterator(iter);
265 }
266
client_match(char * str)267 static rfbClientPtr *client_match(char *str) {
268 rfbClientIteratorPtr iter;
269 rfbClientPtr cl, *cl_list;
270 int i, n, host_warn = 0, hex_warn = 0;
271
272 n = client_count + 10;
273 cl_list = (rfbClientPtr *) malloc(n * sizeof(rfbClientPtr));
274
275 i = 0;
276 iter = rfbGetClientIterator(screen);
277 while( (cl = rfbClientIteratorNext(iter)) ) {
278 ClientData *cd = (ClientData *) cl->clientData;
279 if (strstr(str, "0x") == str) {
280 unsigned int in;
281 int id;
282 if (! cd) {
283 continue;
284 }
285 if (sscanf(str, "0x%x", &in) != 1) {
286 if (hex_warn++) {
287 continue;
288 }
289 rfbLog("skipping invalid client hex id: %s\n",
290 str);
291 continue;
292 }
293 id = (unsigned int) in;
294 if (cd->uid == id) {
295 cl_list[i++] = cl;
296 }
297 } else {
298 int port = -1;
299 char *rstr = strdup(str);
300 char *q = strrchr(rstr, ':');
301 if (q) {
302 port = atoi(q+1);
303 *q = '\0';
304 if (port == 0 && q[1] != '0') {
305 port = -1;
306 } else if (port < 0) {
307 port = -port;
308 } else if (port < 200) {
309 port = 5500 + port;
310 }
311 }
312 if (ipv6_ip(str)) {
313 ;
314 } else if (! dotted_ip(str, 0)) {
315 char *orig = rstr;
316 rstr = host2ip(rstr);
317 free(orig);
318 if (rstr == NULL || *rstr == '\0') {
319 if (host_warn++) {
320 continue;
321 }
322 rfbLog("skipping bad lookup: \"%s\"\n", str);
323 continue;
324 }
325 rfbLog("lookup: %s -> %s port=%d\n", str, rstr, port);
326 }
327 if (!strcmp(rstr, cl->host)) {
328 int ok = 1;
329 if (port > 0) {
330 if (cd != NULL && cd->client_port > 0) {
331 if (cd->client_port != port) {
332 ok = 0;
333 }
334 } else {
335 int cport = get_remote_port(cl->sock);
336 if (cport != port) {
337 ok = 0;
338 }
339 }
340 }
341 if (ok) {
342 cl_list[i++] = cl;
343 }
344 }
345 free(rstr);
346 }
347 if (i >= n - 1) {
348 break;
349 }
350 }
351 rfbReleaseClientIterator(iter);
352
353 cl_list[i] = NULL;
354
355 return cl_list;
356 }
357
close_clients(char * str)358 void close_clients(char *str) {
359 rfbClientPtr *cl_list, *cp;
360
361 if (!strcmp(str, "all") || !strcmp(str, "*")) {
362 close_all_clients();
363 return;
364 }
365
366 if (! screen) {
367 return;
368 }
369
370 cl_list = client_match(str);
371
372 cp = cl_list;
373 while (*cp) {
374 rfbCloseClient(*cp);
375 rfbClientConnectionGone(*cp);
376 cp++;
377 }
378 free(cl_list);
379 }
380
set_client_input(char * str)381 void set_client_input(char *str) {
382 rfbClientPtr *cl_list, *cp;
383 char *p, *val;
384
385 /* str is "match:value" */
386
387 if (! screen) {
388 return;
389 }
390
391 p = strrchr(str, ':');
392 if (! p) {
393 return;
394 }
395 *p = '\0';
396 p++;
397 val = short_kmbcf(p);
398
399 cl_list = client_match(str);
400
401 cp = cl_list;
402 while (*cp) {
403 ClientData *cd = (ClientData *) (*cp)->clientData;
404 if (! cd) {
405 continue;
406 }
407 cd->input[0] = '\0';
408 strcat(cd->input, "_");
409 strcat(cd->input, val);
410 cp++;
411 }
412
413 free(val);
414 free(cl_list);
415 }
416
set_child_info(void)417 void set_child_info(void) {
418 char pid[16];
419 /* set up useful environment for child process */
420 sprintf(pid, "%d", (int) getpid());
421 set_env("X11VNC_PID", pid);
422 if (program_name) {
423 /* e.g. for remote control -R */
424 set_env("X11VNC_PROG", program_name);
425 }
426 if (program_cmdline) {
427 set_env("X11VNC_CMDLINE", program_cmdline);
428 }
429 if (raw_fb_str) {
430 set_env("X11VNC_RAWFB_STR", raw_fb_str);
431 } else {
432 set_env("X11VNC_RAWFB_STR", "");
433 }
434 }
435
cmd_ok(char * cmd)436 int cmd_ok(char *cmd) {
437 char *p, *str;
438 if (no_external_cmds) {
439 return 0;
440 }
441 if (! cmd || cmd[0] == '\0') {
442 return 0;
443 }
444 if (! allowed_external_cmds) {
445 /* default, allow any (overridden by -nocmds) */
446 return 1;
447 }
448
449 str = strdup(allowed_external_cmds);
450 p = strtok(str, ",");
451 while (p) {
452 if (!strcmp(p, cmd)) {
453 free(str);
454 return 1;
455 }
456 p = strtok(NULL, ",");
457 }
458 free(str);
459 return 0;
460 }
461
462 /*
463 * utility to run a user supplied command setting some RFB_ env vars.
464 * used by, e.g., accept_client() and client_gone()
465 */
run_user_command(char * cmd,rfbClientPtr client,char * mode,char * input,int len,FILE * output)466 int run_user_command(char *cmd, rfbClientPtr client, char *mode, char *input,
467 int len, FILE *output) {
468 char *old_display = NULL;
469 char *addr = NULL;
470 char str[100];
471 int rc, ok;
472 ClientData *cd = NULL;
473 client_set_net(client);
474 if (client != NULL) {
475 cd = (ClientData *) client->clientData;
476 addr = client->host;
477 }
478
479 if (addr == NULL || addr[0] == '\0') {
480 addr = "unknown-host";
481 }
482
483 /* set RFB_CLIENT_ID to semi unique id for command to use */
484 if (cd && cd->uid) {
485 sprintf(str, "0x%x", cd->uid);
486 } else {
487 /* not accepted yet: */
488 sprintf(str, "0x%x", clients_served);
489 }
490 set_env("RFB_CLIENT_ID", str);
491
492 /* set RFB_CLIENT_IP to IP addr for command to use */
493 set_env("RFB_CLIENT_IP", addr);
494
495 /* set RFB_X11VNC_PID to our pid for command to use */
496 sprintf(str, "%d", (int) getpid());
497 set_env("RFB_X11VNC_PID", str);
498
499 if (client == NULL) {
500 ;
501 } else if (client->state == RFB_PROTOCOL_VERSION) {
502 set_env("RFB_STATE", "PROTOCOL_VERSION");
503 } else if (client->state == RFB_SECURITY_TYPE) {
504 set_env("RFB_STATE", "SECURITY_TYPE");
505 } else if (client->state == RFB_AUTHENTICATION) {
506 set_env("RFB_STATE", "AUTHENTICATION");
507 } else if (client->state == RFB_INITIALISATION) {
508 set_env("RFB_STATE", "INITIALISATION");
509 } else if (client->state == RFB_NORMAL) {
510 set_env("RFB_STATE", "NORMAL");
511 } else {
512 set_env("RFB_STATE", "UNKNOWN");
513 }
514 if (certret_str) {
515 set_env("RFB_SSL_CLIENT_CERT", certret_str);
516 } else {
517 set_env("RFB_SSL_CLIENT_CERT", "");
518 }
519
520 /* set RFB_CLIENT_PORT to peer port for command to use */
521 if (cd && cd->client_port > 0) {
522 sprintf(str, "%d", cd->client_port);
523 } else if (client) {
524 sprintf(str, "%d", get_remote_port(client->sock));
525 }
526 set_env("RFB_CLIENT_PORT", str);
527
528 set_env("RFB_MODE", mode);
529
530 /*
531 * now do RFB_SERVER_IP and RFB_SERVER_PORT (i.e. us!)
532 * This will establish a 5-tuple (including tcp) the external
533 * program can potentially use to work out the virtual circuit
534 * for this connection.
535 */
536 if (cd && cd->server_ip) {
537 set_env("RFB_SERVER_IP", cd->server_ip);
538 } else if (client) {
539 char *sip = get_local_host(client->sock);
540 set_env("RFB_SERVER_IP", sip);
541 if (sip) free(sip);
542 }
543
544 if (cd && cd->server_port > 0) {
545 sprintf(str, "%d", cd->server_port);
546 } else if (client) {
547 sprintf(str, "%d", get_local_port(client->sock));
548 }
549 set_env("RFB_SERVER_PORT", str);
550
551 if (cd) {
552 sprintf(str, "%d", cd->login_viewonly);
553 } else {
554 sprintf(str, "%d", -1);
555 }
556 set_env("RFB_LOGIN_VIEWONLY", str);
557
558 if (cd) {
559 sprintf(str, "%d", (int) cd->login_time);
560 } else {
561 sprintf(str, ">%d", (int) time(NULL));
562 }
563 set_env("RFB_LOGIN_TIME", str);
564
565 sprintf(str, "%d", (int) time(NULL));
566 set_env("RFB_CURRENT_TIME", str);
567
568 if (!cd || !cd->username || cd->username[0] == '\0') {
569 set_env("RFB_USERNAME", "unknown-user");
570 } else {
571 set_env("RFB_USERNAME", cd->username);
572 }
573 /*
574 * Better set DISPLAY to the one we are polling, if they
575 * want something trickier, they can handle on their own
576 * via environment, etc.
577 */
578 if (getenv("DISPLAY")) {
579 old_display = strdup(getenv("DISPLAY"));
580 }
581
582 if (raw_fb && ! dpy) { /* raw_fb hack */
583 set_env("DISPLAY", "rawfb");
584 } else {
585 set_env("DISPLAY", DisplayString(dpy));
586 }
587
588 /*
589 * work out the number of clients (have to use client_count
590 * since there is deadlock in rfbGetClientIterator)
591 */
592 sprintf(str, "%d", client_count);
593 set_env("RFB_CLIENT_COUNT", str);
594
595 /* gone, accept, afteraccept */
596 ok = 0;
597 if (!strcmp(mode, "env")) {
598 return 1;
599 }
600 if (!strcmp(mode, "accept") && cmd_ok("accept")) {
601 ok = 1;
602 }
603 if (!strcmp(mode, "afteraccept") && cmd_ok("afteraccept")) {
604 ok = 1;
605 }
606 if (!strcmp(mode, "gone") && cmd_ok("gone")) {
607 ok = 1;
608 }
609 if (!strcmp(mode, "cmd_verify") && cmd_ok("unixpw")) {
610 ok = 1;
611 }
612 if (!strcmp(mode, "read_passwds") && cmd_ok("passwdfile")) {
613 ok = 1;
614 }
615 if (!strcmp(mode, "custom_passwd") && cmd_ok("custom_passwd")) {
616 ok = 1;
617 }
618 if (no_external_cmds || !ok) {
619 rfbLogEnable(1);
620 rfbLog("cannot run external commands in -nocmds mode:\n");
621 rfbLog(" \"%s\"\n", cmd);
622 rfbLog(" exiting.\n");
623 clean_up_exit(1);
624 }
625 rfbLog("running command:\n");
626 if (!quiet) {
627 fprintf(stderr, "\n %s\n\n", cmd);
628 }
629 close_exec_fds();
630
631 if (output != NULL) {
632 FILE *ph;
633 char line[1024];
634 char *cmd2 = NULL;
635 char tmp[] = "/tmp/x11vnc-tmp.XXXXXX";
636 int deltmp = 0;
637
638 if (input != NULL) {
639 int tmp_fd = mkstemp(tmp);
640 if (tmp_fd < 0) {
641 rfbLog("mkstemp failed on: %s\n", tmp);
642 clean_up_exit(1);
643 }
644 write(tmp_fd, input, len);
645 close(tmp_fd);
646 deltmp = 1;
647 cmd2 = (char *) malloc(100 + strlen(tmp) + strlen(cmd));
648 sprintf(cmd2, "/bin/cat %s | %s", tmp, cmd);
649
650 ph = popen(cmd2, "r");
651 } else {
652 ph = popen(cmd, "r");
653 }
654 if (ph == NULL) {
655 rfbLog("popen(%s) failed", cmd);
656 rfbLogPerror("popen");
657 clean_up_exit(1);
658 }
659 memset(line, 0, sizeof(line));
660 while (fgets(line, sizeof(line), ph) != NULL) {
661 int j, k = -1;
662 if (0) fprintf(stderr, "line: %s", line);
663 /* take care to handle embedded nulls */
664 for (j=0; j < (int) sizeof(line); j++) {
665 if (line[j] != '\0') {
666 k = j;
667 }
668 }
669 if (k >= 0) {
670 write(fileno(output), line, k+1);
671 }
672 memset(line, 0, sizeof(line));
673 }
674
675 rc = pclose(ph);
676
677 if (cmd2 != NULL) {
678 free(cmd2);
679 }
680 if (deltmp) {
681 unlink(tmp);
682 }
683 goto got_rc;
684 } else if (input != NULL) {
685 FILE *ph = popen(cmd, "w");
686 if (ph == NULL) {
687 rfbLog("popen(%s) failed", cmd);
688 rfbLogPerror("popen");
689 clean_up_exit(1);
690 }
691 write(fileno(ph), input, len);
692 rc = pclose(ph);
693 goto got_rc;
694 }
695
696 #if LIBVNCSERVER_HAVE_FORK
697 {
698 pid_t pid;
699 struct sigaction sa, intr, quit;
700 sigset_t omask;
701
702 sa.sa_handler = SIG_IGN;
703 sa.sa_flags = 0;
704 sigemptyset(&sa.sa_mask);
705 sigaction(SIGINT, &sa, &intr);
706 sigaction(SIGQUIT, &sa, &quit);
707
708 sigaddset(&sa.sa_mask, SIGCHLD);
709 sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask);
710
711 if ((pid = fork()) > 0 || pid == -1) {
712
713 if (pid != -1) {
714 waitpid(pid, &rc, 0);
715 }
716
717 sigaction(SIGINT, &intr, (struct sigaction *) NULL);
718 sigaction(SIGQUIT, &quit, (struct sigaction *) NULL);
719 sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL);
720
721 if (pid == -1) {
722 fprintf(stderr, "could not fork\n");
723 rfbLogPerror("fork");
724 rc = system(cmd);
725 }
726 } else {
727 /* this should close port 5900, etc.. */
728 int fd;
729 sigaction(SIGINT, &intr, (struct sigaction *) NULL);
730 sigaction(SIGQUIT, &quit, (struct sigaction *) NULL);
731 sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL);
732 for (fd=3; fd<256; fd++) {
733 close(fd);
734 }
735 /* XXX test more */
736 if (!strcmp(mode, "gone")) {
737 #if HAVE_SETSID
738 setsid();
739 #else
740 setpgrp();
741 #endif
742 }
743 execlp("/bin/sh", "/bin/sh", "-c", cmd, (char *) NULL);
744 exit(1);
745 }
746 }
747 #else
748 rc = system(cmd);
749 #endif
750 got_rc:
751
752 if (rc >= 256) {
753 rc = rc/256;
754 }
755 rfbLog("command returned: %d\n", rc);
756
757 if (old_display) {
758 set_env("DISPLAY", old_display);
759 free(old_display);
760 }
761
762 return rc;
763 }
764
free_client_data(rfbClientPtr client)765 static void free_client_data(rfbClientPtr client) {
766 if (! client) {
767 return;
768 }
769 if (client->clientData) {
770 ClientData *cd = (ClientData *) client->clientData;
771 if (cd) {
772 if (cd->server_ip) {
773 free(cd->server_ip);
774 cd->server_ip = NULL;
775 }
776 if (cd->hostname) {
777 free(cd->hostname);
778 cd->hostname = NULL;
779 }
780 if (cd->username) {
781 free(cd->username);
782 cd->username = NULL;
783 }
784 if (cd->unixname) {
785 free(cd->unixname);
786 cd->unixname = NULL;
787 }
788 if (cd->cursor) {
789 rfbFreeCursor(cd->cursor);
790 cd->cursor = NULL;
791 }
792 if (cd->under_cursor_buffer) {
793 free(cd->under_cursor_buffer);
794 cd->under_cursor_buffer = NULL;
795 }
796 if (cd->cursor_region) {
797 sraRgnDestroy(cd->cursor_region);
798 cd->cursor_region = NULL;
799 }
800 }
801 free(client->clientData);
802 client->clientData = NULL;
803 }
804 }
805
806 static int accepted_client = 0;
807
808 /*
809 * callback for when a client disconnects
810 */
client_gone(rfbClientPtr client)811 void client_gone(rfbClientPtr client) {
812 ClientData *cd = NULL;
813
814 CLIENT_LOCK;
815
816 client_count--;
817 if (client_count < 0) client_count = 0;
818
819 speeds_net_rate_measured = 0;
820 speeds_net_latency_measured = 0;
821
822 rfbLog("client_count: %d\n", client_count);
823 last_client_gone = dnow();
824
825 if (unixpw_in_progress && unixpw_client) {
826 if (client == unixpw_client) {
827 unixpw_in_progress = 0;
828 /* mutex */
829 screen->permitFileTransfer = unixpw_file_xfer_save;
830 if ((tightfilexfer = unixpw_tightvnc_xfer_save)) {
831 #ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER
832 rfbLog("rfbRegisterTightVNCFileTransferExtension: 3\n");
833 rfbRegisterTightVNCFileTransferExtension();
834 #endif
835 }
836 unixpw_client = NULL;
837 copy_screen();
838 }
839 }
840
841
842 if (no_autorepeat && client_count == 0) {
843 autorepeat(1, 0);
844 }
845 if (use_solid_bg && client_count == 0) {
846 solid_bg(1);
847 }
848 if ((ncache || ncache0) && client_count == 0) {
849 kde_no_animate(1);
850 }
851 if (client->clientData) {
852 cd = (ClientData *) client->clientData;
853 if (cd->ssl_helper_pid > 0) {
854 int status;
855 rfbLog("sending SIGTERM to ssl_helper_pid: %d\n",
856 cd->ssl_helper_pid);
857 kill(cd->ssl_helper_pid, SIGTERM);
858 usleep(200*1000);
859 #if LIBVNCSERVER_HAVE_SYS_WAIT_H && HAVE_WAITPID
860 waitpid(cd->ssl_helper_pid, &status, WNOHANG);
861 #endif
862 ssl_helper_pid(cd->ssl_helper_pid, -1); /* delete */
863 }
864 }
865 if (gone_cmd && *gone_cmd != '\0') {
866 if (strstr(gone_cmd, "popup") == gone_cmd) {
867 int x = -64000, y = -64000, timeout = 120;
868 char *userhost = ident_username(client);
869 char *addr, *p, *mode;
870
871 /* extract timeout */
872 if ((p = strchr(gone_cmd, ':')) != NULL) {
873 int in;
874 if (sscanf(p+1, "%d", &in) == 1) {
875 timeout = in;
876 }
877 }
878 /* extract geometry */
879 if ((p = strpbrk(gone_cmd, "+-")) != NULL) {
880 ugly_geom(p, &x, &y);
881 }
882
883 /* find mode: mouse, key, or both */
884 if (strstr(gone_cmd, "popupmouse") == gone_cmd) {
885 mode = "mouse_only";
886 } else if (strstr(gone_cmd, "popupkey") == gone_cmd) {
887 mode = "key_only";
888 } else {
889 mode = "both";
890 }
891
892 addr = client->host;
893
894 ugly_window(addr, userhost, x, y, timeout, mode, 0);
895
896 free(userhost);
897 } else {
898 rfbLog("client_gone: using cmd: %s\n", client->host);
899 run_user_command(gone_cmd, client, "gone", NULL, 0, NULL);
900 }
901 }
902
903 /* remove clients XInput2 master device */
904 if(use_multipointer)
905 if(removeMD(dpy, cd->ptr_id))
906 rfbLog("removed XInput2 MD for client %s.\n", client->host);
907
908 free_client_data(client);
909
910 if (inetd && client == inetd_client) {
911 rfbLog("inetd viewer exited.\n");
912 if (gui_pid > 0) {
913 rfbLog("killing gui_pid %d\n", gui_pid);
914 kill(gui_pid, SIGTERM);
915 }
916 clean_up_exit(0);
917 }
918
919 if (connect_once) {
920 /*
921 * This non-exit is done for a bad passwd to be consistent
922 * with our RFB_CLIENT_REFUSE behavior in new_client() (i.e.
923 * we disconnect after 1 successful connection).
924 */
925 if ((client->state == RFB_PROTOCOL_VERSION ||
926 client->state == RFB_SECURITY_TYPE ||
927 client->state == RFB_AUTHENTICATION ||
928 client->state == RFB_INITIALISATION) && accepted_client) {
929 rfbLog("connect_once: invalid password or early "
930 "disconnect. %d\n", client->state);
931 rfbLog("connect_once: waiting for next connection.\n");
932 accepted_client--;
933 if (accepted_client < 0) {
934 accepted_client = 0;
935 }
936 CLIENT_UNLOCK;
937 if (connect_or_exit) {
938 clean_up_exit(1);
939 }
940 return;
941 }
942 if (shared && client_count > 0) {
943 rfbLog("connect_once: other shared clients still "
944 "connected, not exiting.\n");
945 CLIENT_UNLOCK;
946 return;
947 }
948
949 rfbLog("viewer exited.\n");
950 if ((client_connect || connect_or_exit) && gui_pid > 0) {
951 rfbLog("killing gui_pid %d\n", gui_pid);
952 kill(gui_pid, SIGTERM);
953 }
954 CLIENT_UNLOCK;
955 clean_up_exit(0);
956 }
957 #ifdef MACOSX
958 if (macosx_console && client_count == 0) {
959 macosxCG_refresh_callback_off();
960 }
961 #endif
962 CLIENT_UNLOCK;
963 }
964
965 /*
966 * Simple routine to limit access via string compare. A power user will
967 * want to compile libvncserver with libwrap support and use /etc/hosts.allow.
968 */
check_access(char * addr)969 int check_access(char *addr) {
970 int allowed = 0;
971 int ssl = 0;
972 char *p, *list;
973
974 if (use_openssl || use_stunnel) {
975 ssl = 1;
976 }
977 if (deny_all) {
978 rfbLog("check_access: new connections are currently "
979 "blocked.\n");
980 return 0;
981 }
982 if (addr == NULL || *addr == '\0') {
983 rfbLog("check_access: denying empty host IP address string.\n");
984 return 0;
985 }
986
987 if (allow_list == NULL) {
988 /* set to "" to possibly append allow_once */
989 allow_list = strdup("");
990 }
991 if (*allow_list == '\0' && allow_once == NULL) {
992 /* no constraints, accept it */
993 return 1;
994 }
995
996 if (strchr(allow_list, '/')) {
997 /* a file of IP addresess or prefixes */
998 int len, len2 = 0;
999 struct stat sbuf;
1000 FILE *in;
1001 char line[1024], *q;
1002
1003 if (stat(allow_list, &sbuf) != 0) {
1004 rfbLogEnable(1);
1005 rfbLog("check_access: failure stating file: %s\n",
1006 allow_list);
1007 rfbLogPerror("stat");
1008 clean_up_exit(1);
1009 }
1010 len = sbuf.st_size + 1; /* 1 more for '\0' at end */
1011 if (allow_once) {
1012 len2 = strlen(allow_once) + 2;
1013 len += len2;
1014 }
1015 if (ssl) {
1016 len2 = strlen("127.0.0.1") + 2;
1017 len += len2;
1018 }
1019 list = (char *) malloc(len);
1020 list[0] = '\0';
1021
1022 in = fopen(allow_list, "r");
1023 if (in == NULL) {
1024 rfbLogEnable(1);
1025 rfbLog("check_access: cannot open: %s\n", allow_list);
1026 rfbLogPerror("fopen");
1027 clean_up_exit(1);
1028 }
1029 while (fgets(line, 1024, in) != NULL) {
1030 if ( (q = strchr(line, '#')) != NULL) {
1031 *q = '\0';
1032 }
1033 if (strlen(list) + strlen(line) >=
1034 (size_t) (len - len2)) {
1035 /* file grew since our stat() */
1036 break;
1037 }
1038 strcat(list, line);
1039 }
1040 fclose(in);
1041 if (allow_once) {
1042 strcat(list, "\n");
1043 strcat(list, allow_once);
1044 strcat(list, "\n");
1045 }
1046 if (ssl) {
1047 strcat(list, "\n");
1048 strcat(list, "127.0.0.1");
1049 strcat(list, "\n");
1050 }
1051 } else {
1052 int len = strlen(allow_list) + 1;
1053 if (allow_once) {
1054 len += strlen(allow_once) + 1;
1055 }
1056 if (ssl) {
1057 len += strlen("127.0.0.1") + 1;
1058 }
1059 list = (char *) malloc(len);
1060 list[0] = '\0';
1061 strcat(list, allow_list);
1062 if (allow_once) {
1063 strcat(list, ",");
1064 strcat(list, allow_once);
1065 }
1066 if (ssl) {
1067 strcat(list, ",");
1068 strcat(list, "127.0.0.1");
1069 }
1070 }
1071
1072 if (allow_once) {
1073 free(allow_once);
1074 allow_once = NULL;
1075 }
1076
1077 p = strtok(list, ", \t\n\r");
1078 while (p) {
1079 char *chk, *q, *r = NULL;
1080 if (*p == '\0') {
1081 p = strtok(NULL, ", \t\n\r");
1082 continue;
1083 }
1084 if (ipv6_ip(p)) {
1085 chk = p;
1086 } else if (! dotted_ip(p, 1)) {
1087 r = host2ip(p);
1088 if (r == NULL || *r == '\0') {
1089 rfbLog("check_access: bad lookup \"%s\"\n", p);
1090 p = strtok(NULL, ", \t\n\r");
1091 continue;
1092 }
1093 rfbLog("check_access: lookup %s -> %s\n", p, r);
1094 chk = r;
1095 } else {
1096 chk = p;
1097 }
1098 if (getenv("X11VNC_DEBUG_ACCESS")) fprintf(stderr, "chk: %s part: %s addr: %s\n", chk, p, addr);
1099
1100 q = strstr(addr, chk);
1101 if (ipv6_ip(addr)) {
1102 if (!strcmp(chk, "localhost") && !strcmp(addr, "::1")) {
1103 rfbLog("check_access: client addr %s is local.\n", addr);
1104 allowed = 1;
1105 } else if (!strcmp(chk, "::1") && !strcmp(addr, "::1")) {
1106 rfbLog("check_access: client addr %s is local.\n", addr);
1107 allowed = 1;
1108 } else if (!strcmp(chk, "127.0.0.1") && !strcmp(addr, "::1")) {
1109 /* this if for host2ip("localhost") */
1110 rfbLog("check_access: client addr %s is local.\n", addr);
1111 allowed = 1;
1112 } else if (q == addr) {
1113 rfbLog("check_access: client %s matches pattern %s\n", addr, chk);
1114 allowed = 1;
1115 }
1116 } else if (chk[strlen(chk)-1] != '.') {
1117 if (!strcmp(addr, chk)) {
1118 if (chk != p) {
1119 rfbLog("check_access: client %s " "matches host %s=%s\n", addr, chk, p);
1120 } else {
1121 rfbLog("check_access: client %s " "matches host %s\n", addr, chk);
1122 }
1123 allowed = 1;
1124 } else if(!strcmp(chk, "localhost") && !strcmp(addr, "127.0.0.1")) {
1125 allowed = 1;
1126 }
1127 } else if (q == addr) {
1128 rfbLog("check_access: client %s matches pattern %s\n", addr, chk);
1129 allowed = 1;
1130 }
1131 p = strtok(NULL, ", \t\n\r");
1132 if (r) {
1133 free(r);
1134 }
1135 if (allowed) {
1136 break;
1137 }
1138 }
1139 free(list);
1140 return allowed;
1141 }
1142
1143 /*
1144 * x11vnc's first (and only) visible widget: accept/reject dialog window.
1145 * We go through this pain to avoid dependency on libXt...
1146 */
ugly_window(char * addr,char * userhost,int X,int Y,int timeout,char * mode,int accept)1147 static int ugly_window(char *addr, char *userhost, int X, int Y,
1148 int timeout, char *mode, int accept) {
1149 #if NO_X11
1150 if (!addr || !userhost || !X || !Y || !timeout || !mode || !accept) {}
1151 RAWFB_RET(0)
1152 nox11_exit(1);
1153 return 0;
1154 #else
1155
1156 #define t2x2_width 16
1157 #define t2x2_height 16
1158 static unsigned char t2x2_bits[] = {
1159 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff,
1160 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33,
1161 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33};
1162
1163 Window awin;
1164 GC gc;
1165 XSizeHints hints;
1166 XGCValues values;
1167 static XFontStruct *font_info = NULL;
1168 static Pixmap ico = 0;
1169 unsigned long valuemask = 0;
1170 static char dash_list[] = {20, 40};
1171 int list_length = sizeof(dash_list);
1172
1173 Atom wm_protocols;
1174 Atom wm_delete_window;
1175
1176 XEvent ev;
1177 long evmask = ExposureMask | KeyPressMask | ButtonPressMask
1178 | StructureNotifyMask;
1179 double waited = 0.0;
1180
1181 /* strings and geometries y/n */
1182 KeyCode key_y, key_n, key_v;
1183 char strh[100];
1184 char stri[100];
1185 char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button";
1186 char str2_b[] = "To reject: press \"n\" or click the \"No\" button";
1187 char str3_b[] = "View only: press \"v\" or click the \"View\" button";
1188 char str1_m[] = "To accept: click the \"Yes\" button";
1189 char str2_m[] = "To reject: click the \"No\" button";
1190 char str3_m[] = "View only: click the \"View\" button";
1191 char str1_k[] = "To accept: press \"y\"";
1192 char str2_k[] = "To reject: press \"n\"";
1193 char str3_k[] = "View only: press \"v\"";
1194 char *str1, *str2, *str3;
1195 char str_y[] = "Yes";
1196 char str_n[] = "No";
1197 char str_v[] = "View";
1198 int x, y, w = 345, h = 175, ret = 0;
1199 int X_sh = 20, Y_sh = 30, dY = 20;
1200 int Ye_x = 20, Ye_y = 0, Ye_w = 45, Ye_h = 20;
1201 int No_x = 75, No_y = 0, No_w = 45, No_h = 20;
1202 int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20;
1203 char *sprop = "new x11vnc client";
1204
1205 KeyCode key_o;
1206
1207 RAWFB_RET(0)
1208
1209 if (! accept) {
1210 sprintf(str_y, "OK");
1211 sprop = "x11vnc client disconnected";
1212 h = 110;
1213 str1 = "";
1214 str2 = "";
1215 str3 = "";
1216 } else if (!strcmp(mode, "mouse_only")) {
1217 str1 = str1_m;
1218 str2 = str2_m;
1219 str3 = str3_m;
1220 } else if (!strcmp(mode, "key_only")) {
1221 str1 = str1_k;
1222 str2 = str2_k;
1223 str3 = str3_k;
1224 h -= dY;
1225 } else {
1226 str1 = str1_b;
1227 str2 = str2_b;
1228 str3 = str3_b;
1229 }
1230 if (view_only) {
1231 h -= dY;
1232 }
1233
1234 /* XXX handle coff_x/coff_y? */
1235 if (X < -dpy_x) {
1236 x = (dpy_x - w)/2; /* large negative: center */
1237 if (x < 0) x = 0;
1238 } else if (X < 0) {
1239 x = dpy_x + X - w; /* from lower right */
1240 } else {
1241 x = X; /* from upper left */
1242 }
1243
1244 if (Y < -dpy_y) {
1245 y = (dpy_y - h)/2;
1246 if (y < 0) y = 0;
1247 } else if (Y < 0) {
1248 y = dpy_y + Y - h;
1249 } else {
1250 y = Y;
1251 }
1252
1253 X_LOCK;
1254
1255 awin = XCreateSimpleWindow(dpy, window, x, y, w, h, 4,
1256 BlackPixel(dpy, scr), WhitePixel(dpy, scr));
1257
1258 wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
1259 wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1260 XSetWMProtocols(dpy, awin, &wm_delete_window, 1);
1261
1262 if (! ico) {
1263 ico = XCreateBitmapFromData(dpy, awin, (char *) t2x2_bits,
1264 t2x2_width, t2x2_height);
1265 }
1266
1267 hints.flags = PPosition | PSize | PMinSize;
1268 hints.x = x;
1269 hints.y = y;
1270 hints.width = w;
1271 hints.height = h;
1272 hints.min_width = w;
1273 hints.min_height = h;
1274
1275 XSetStandardProperties(dpy, awin, sprop, "x11vnc query", ico, NULL,
1276 0, &hints);
1277
1278 XSelectInput_wr(dpy, awin, evmask);
1279
1280 if (! font_info && (font_info = XLoadQueryFont(dpy, "fixed")) == NULL) {
1281 rfbLogEnable(1);
1282 rfbLog("ugly_window: cannot locate font fixed.\n");
1283 X_UNLOCK;
1284 clean_up_exit(1);
1285 }
1286
1287 gc = XCreateGC(dpy, awin, valuemask, &values);
1288 XSetFont(dpy, gc, font_info->fid);
1289 XSetForeground(dpy, gc, BlackPixel(dpy, scr));
1290 XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
1291 XSetDashes(dpy, gc, 0, dash_list, list_length);
1292
1293 XMapWindow(dpy, awin);
1294 XFlush_wr(dpy);
1295
1296 if (accept) {
1297 char *ip = addr;
1298 char *type = "accept";
1299 if (unixpw && strstr(userhost, "UNIX:") != userhost) {
1300 type = "UNIXPW";
1301 if (openssl_last_ip) {
1302 ip = openssl_last_ip;
1303 }
1304 }
1305 snprintf(strh, sizeof strh, "x11vnc: %s connection from %s?", type, ip);
1306 } else {
1307 snprintf(strh, sizeof strh, "x11vnc: client disconnected from %s", addr);
1308 }
1309 snprintf(stri, sizeof stri, " (%s)", userhost);
1310
1311 key_o = XKeysymToKeycode(dpy, XStringToKeysym("o"));
1312 key_y = XKeysymToKeycode(dpy, XStringToKeysym("y"));
1313 key_n = XKeysymToKeycode(dpy, XStringToKeysym("n"));
1314 key_v = XKeysymToKeycode(dpy, XStringToKeysym("v"));
1315
1316 while (1) {
1317 int out = -1, x, y, tw, k;
1318
1319 if (XCheckWindowEvent(dpy, awin, evmask, &ev)) {
1320 ; /* proceed to handling */
1321 } else if (XCheckTypedEvent(dpy, ClientMessage, &ev)) {
1322 ; /* proceed to handling */
1323 } else {
1324 int ms = 100; /* sleep a bit */
1325 usleep(ms * 1000);
1326 waited += ((double) ms)/1000.;
1327 if (timeout && (int) waited >= timeout) {
1328 rfbLog("ugly_window: popup timed out after "
1329 "%d seconds.\n", timeout);
1330 out = 0;
1331 ev.type = 0;
1332 } else {
1333 continue;
1334 }
1335 }
1336
1337 switch(ev.type) {
1338 case Expose:
1339 while (XCheckTypedEvent(dpy, Expose, &ev)) {
1340 ;
1341 }
1342 k=0;
1343
1344 /* instructions */
1345 XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
1346 strh, strlen(strh));
1347 XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
1348 stri, strlen(stri));
1349 if (accept) {
1350 XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
1351 str1, strlen(str1));
1352 XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
1353 str2, strlen(str2));
1354 if (! view_only) {
1355 XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
1356 str3, strlen(str3));
1357 }
1358 }
1359
1360 if (!strcmp(mode, "key_only")) {
1361 break;
1362 }
1363
1364 /* buttons */
1365 Ye_y = Y_sh+k*dY;
1366 No_y = Y_sh+k*dY;
1367 Vi_y = Y_sh+k*dY;
1368 XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h);
1369
1370 if (accept) {
1371 XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h);
1372 if (! view_only) {
1373 XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y,
1374 Vi_w, Vi_h);
1375 }
1376 }
1377
1378 tw = XTextWidth(font_info, str_y, strlen(str_y));
1379 tw = (Ye_w - tw)/2;
1380 if (tw < 0) tw = 1;
1381 XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5,
1382 str_y, strlen(str_y));
1383
1384 if (!accept) {
1385 break;
1386 }
1387 tw = XTextWidth(font_info, str_n, strlen(str_n));
1388 tw = (No_w - tw)/2;
1389 if (tw < 0) tw = 1;
1390 XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5,
1391 str_n, strlen(str_n));
1392
1393 if (! view_only) {
1394 tw = XTextWidth(font_info, str_v,
1395 strlen(str_v));
1396 tw = (Vi_w - tw)/2;
1397 if (tw < 0) tw = 1;
1398 XDrawString(dpy, awin, gc, Vi_x+tw,
1399 Vi_y+Vi_h-5, str_v, strlen(str_v));
1400 }
1401
1402 break;
1403
1404 case ClientMessage:
1405 if (ev.xclient.message_type == wm_protocols &&
1406 (Atom) ev.xclient.data.l[0] == wm_delete_window) {
1407 out = 0;
1408 }
1409 break;
1410
1411 case ButtonPress:
1412 x = ev.xbutton.x;
1413 y = ev.xbutton.y;
1414 if (!strcmp(mode, "key_only")) {
1415 ;
1416 } else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y
1417 && y < Ye_y+Ye_h) {
1418 out = 1;
1419 } else if (! accept) {
1420 ;
1421 } else if (x > No_x && x < No_x+No_w && y > No_y
1422 && y < No_y+No_h) {
1423 out = 0;
1424 } else if (! view_only && x > Vi_x && x < Vi_x+Vi_w
1425 && y > Vi_y && y < Vi_y+Ye_h) {
1426 out = 2;
1427 }
1428 break;
1429
1430 case KeyPress:
1431 if (!strcmp(mode, "mouse_only")) {
1432 ;
1433 } else if (! accept) {
1434 if (ev.xkey.keycode == key_o) {
1435 out = 1;
1436 }
1437 if (ev.xkey.keycode == key_y) {
1438 out = 1;
1439 }
1440 } else if (ev.xkey.keycode == key_y) {
1441 out = 1;
1442 ;
1443 } else if (ev.xkey.keycode == key_n) {
1444 out = 0;
1445 } else if (! view_only && ev.xkey.keycode == key_v) {
1446 out = 2;
1447 }
1448 break;
1449 default:
1450 break;
1451 }
1452 if (out != -1) {
1453 ret = out;
1454 XSelectInput_wr(dpy, awin, 0);
1455 XUnmapWindow(dpy, awin);
1456 XFree_wr(gc);
1457 XDestroyWindow(dpy, awin);
1458 XFlush_wr(dpy);
1459 break;
1460 }
1461 }
1462 X_UNLOCK;
1463
1464 return ret;
1465 #endif /* NO_X11 */
1466 }
1467
1468 /*
1469 * process a "yes:0,no:*,view:3" type action list comparing to command
1470 * return code rc. * means the default action with no other match.
1471 */
action_match(char * action,int rc)1472 static int action_match(char *action, int rc) {
1473 char *p, *q, *s = strdup(action);
1474 int cases[4], i, result;
1475 char *labels[4];
1476
1477 labels[1] = "yes";
1478 labels[2] = "no";
1479 labels[3] = "view";
1480
1481 rfbLog("accept_client: process action line: %s\n",
1482 action);
1483
1484 for (i=1; i <= 3; i++) {
1485 cases[i] = -2;
1486 }
1487
1488 p = strtok(s, ",");
1489 while (p) {
1490 if ((q = strchr(p, ':')) != NULL) {
1491 int in, k = 1;
1492 *q = '\0';
1493 q++;
1494 if (strstr(p, "yes") == p) {
1495 k = 1;
1496 } else if (strstr(p, "no") == p) {
1497 k = 2;
1498 } else if (strstr(p, "view") == p) {
1499 k = 3;
1500 } else {
1501 rfbLogEnable(1);
1502 rfbLog("invalid action line: %s\n", action);
1503 clean_up_exit(1);
1504 }
1505 if (*q == '*') {
1506 cases[k] = -1;
1507 } else if (sscanf(q, "%d", &in) == 1) {
1508 if (in < 0) {
1509 rfbLogEnable(1);
1510 rfbLog("invalid action line: %s\n",
1511 action);
1512 clean_up_exit(1);
1513 }
1514 cases[k] = in;
1515 } else {
1516 rfbLogEnable(1);
1517 rfbLog("invalid action line: %s\n", action);
1518 clean_up_exit(1);
1519 }
1520 } else {
1521 rfbLogEnable(1);
1522 rfbLog("invalid action line: %s\n", action);
1523 clean_up_exit(1);
1524 }
1525 p = strtok(NULL, ",");
1526 }
1527 free(s);
1528
1529 result = -1;
1530 for (i=1; i <= 3; i++) {
1531 if (cases[i] == -1) {
1532 rfbLog("accept_client: default action is case=%d %s\n",
1533 i, labels[i]);
1534 result = i;
1535 break;
1536 }
1537 }
1538 if (result == -1) {
1539 rfbLog("accept_client: no default action\n");
1540 }
1541 for (i=1; i <= 3; i++) {
1542 if (cases[i] >= 0 && cases[i] == rc) {
1543 rfbLog("accept_client: matched action is case=%d %s\n",
1544 i, labels[i]);
1545 result = i;
1546 break;
1547 }
1548 }
1549 if (result < 0) {
1550 rfbLog("no action match: %s rc=%d set to no\n", action, rc);
1551 result = 2;
1552 }
1553 return result;
1554 }
1555
ugly_geom(char * p,int * x,int * y)1556 static void ugly_geom(char *p, int *x, int *y) {
1557 int x1, y1;
1558
1559 if (sscanf(p, "+%d+%d", &x1, &y1) == 2) {
1560 *x = x1;
1561 *y = y1;
1562 } else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) {
1563 *x = x1;
1564 *y = -y1;
1565 } else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) {
1566 *x = -x1;
1567 *y = y1;
1568 } else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) {
1569 *x = -x1;
1570 *y = -y1;
1571 }
1572 }
1573
1574 /*
1575 * Simple routine to prompt the user on the X display whether an incoming
1576 * client should be allowed to connect or not. If a gui is involved it
1577 * will be running in the environment/context of the X11 DISPLAY.
1578 *
1579 * The command supplied via -accept is run as is (i.e. no string
1580 * substitution) with the RFB_CLIENT_IP environment variable set to the
1581 * incoming client's numerical IP address.
1582 *
1583 * If the external command exits with 0 the client is accepted, otherwise
1584 * the client is rejected.
1585 *
1586 * Some builtins are provided:
1587 *
1588 * xmessage: use homebrew xmessage(1) for the external command.
1589 * popup: use internal X widgets for prompting.
1590 *
1591 */
accept_client(rfbClientPtr client)1592 int accept_client(rfbClientPtr client) {
1593
1594 char xmessage[200], *cmd = NULL;
1595 char *addr = client->host;
1596 char *action = NULL;
1597
1598 if (accept_cmd == NULL || *accept_cmd == '\0') {
1599 return 1; /* no command specified, so we accept */
1600 }
1601
1602 if (addr == NULL || addr[0] == '\0') {
1603 addr = "unknown-host";
1604 }
1605
1606 if (strstr(accept_cmd, "popup") == accept_cmd) {
1607 /* use our builtin popup button */
1608
1609 /* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */
1610
1611 int ret, timeout = 120;
1612 int x = -64000, y = -64000;
1613 char *p, *mode;
1614 char *userhost = ident_username(client);
1615
1616 /* extract timeout */
1617 if ((p = strchr(accept_cmd, ':')) != NULL) {
1618 int in;
1619 if (sscanf(p+1, "%d", &in) == 1) {
1620 timeout = in;
1621 }
1622 }
1623 /* extract geometry */
1624 if ((p = strpbrk(accept_cmd, "+-")) != NULL) {
1625 ugly_geom(p, &x, &y);
1626 }
1627
1628 /* find mode: mouse, key, or both */
1629 if (strstr(accept_cmd, "popupmouse") == accept_cmd) {
1630 mode = "mouse_only";
1631 } else if (strstr(accept_cmd, "popupkey") == accept_cmd) {
1632 mode = "key_only";
1633 } else {
1634 mode = "both";
1635 }
1636
1637 if (dpy == NULL && use_dpy && strstr(use_dpy, "WAIT:") ==
1638 use_dpy) {
1639 rfbLog("accept_client: warning allowing client under conditions:\n");
1640 rfbLog(" -display WAIT:, dpy == NULL, -accept popup.\n");
1641 rfbLog(" There will be another popup.\n");
1642 return 1;
1643 }
1644
1645 rfbLog("accept_client: using builtin popup for: %s\n", addr);
1646 if ((ret = ugly_window(addr, userhost, x, y, timeout,
1647 mode, 1))) {
1648 free(userhost);
1649 if (ret == 2) {
1650 rfbLog("accept_client: viewonly: %s\n", addr);
1651 client->viewOnly = TRUE;
1652 }
1653 rfbLog("accept_client: popup accepted: %s\n", addr);
1654 return 1;
1655 } else {
1656 free(userhost);
1657 rfbLog("accept_client: popup rejected: %s\n", addr);
1658 return 0;
1659 }
1660
1661 } else if (!strcmp(accept_cmd, "xmessage")) {
1662 /* make our own command using xmessage(1) */
1663
1664 if (view_only) {
1665 sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center"
1666 " 'x11vnc: accept connection from %s?'", addr);
1667 } else {
1668 sprintf(xmessage, "xmessage -buttons yes:0,no:2,"
1669 "view-only:3 -center" " 'x11vnc: accept connection"
1670 " from %s?'", addr);
1671 action = "yes:0,no:*,view:3";
1672 }
1673 cmd = xmessage;
1674
1675 } else {
1676 /* use the user supplied command: */
1677
1678 cmd = accept_cmd;
1679
1680 /* extract any action prefix: yes:N,no:M,view:K */
1681 if (strstr(accept_cmd, "yes:") == accept_cmd) {
1682 char *p;
1683 if ((p = strpbrk(accept_cmd, " \t")) != NULL) {
1684 int i;
1685 cmd = p;
1686 p = accept_cmd;
1687 for (i=0; i<200; i++) {
1688 if (*p == ' ' || *p == '\t') {
1689 xmessage[i] = '\0';
1690 break;
1691 }
1692 xmessage[i] = *p;
1693 p++;
1694 }
1695 xmessage[200-1] = '\0';
1696 action = xmessage;
1697 }
1698 }
1699 }
1700
1701 if (cmd) {
1702 int rc;
1703
1704 rfbLog("accept_client: using cmd for: %s\n", addr);
1705 rc = run_user_command(cmd, client, "accept", NULL, 0, NULL);
1706
1707 if (action) {
1708 int result;
1709
1710 if (rc < 0) {
1711 rfbLog("accept_client: cannot use negative "
1712 "rc: %d, action %s\n", rc, action);
1713 result = 2;
1714 } else {
1715 result = action_match(action, rc);
1716 }
1717
1718 if (result == 1) {
1719 rc = 0;
1720 } else if (result == 2) {
1721 rc = 1;
1722 } else if (result == 3) {
1723 rc = 0;
1724 rfbLog("accept_client: viewonly: %s\n", addr);
1725 client->viewOnly = TRUE;
1726 } else {
1727 rc = 1; /* NOTREACHED */
1728 }
1729 }
1730
1731 if (rc == 0) {
1732 rfbLog("accept_client: accepted: %s\n", addr);
1733 return 1;
1734 } else {
1735 rfbLog("accept_client: rejected: %s\n", addr);
1736 return 0;
1737 }
1738 } else {
1739 rfbLog("accept_client: no command, rejecting %s\n", addr);
1740 return 0;
1741 }
1742
1743 /* return 0; NOTREACHED */
1744 }
1745
check_ipv6_listen(long usec)1746 void check_ipv6_listen(long usec) {
1747 #if X11VNC_IPV6
1748 fd_set fds;
1749 struct timeval tv;
1750 int nfds, csock = -1, one = 1;
1751 struct sockaddr_in6 addr;
1752 socklen_t addrlen = sizeof(addr);
1753 rfbClientPtr cl;
1754 int nmax = 0;
1755 char *name;
1756
1757 if (!ipv6_listen || noipv6) {
1758 return;
1759 }
1760 if (ipv6_listen_fd < 0 && ipv6_http_fd < 0) {
1761 return;
1762 }
1763
1764 FD_ZERO(&fds);
1765 if (ipv6_listen_fd >= 0) {
1766 FD_SET(ipv6_listen_fd, &fds);
1767 nmax = ipv6_listen_fd;
1768 }
1769 if (ipv6_http_fd >= 0 && screen->httpSock < 0) {
1770 FD_SET(ipv6_http_fd, &fds);
1771 if (ipv6_http_fd > nmax) {
1772 nmax = ipv6_http_fd;
1773 }
1774 }
1775
1776 tv.tv_sec = 0;
1777 tv.tv_usec = 0;
1778
1779 nfds = select(nmax+1, &fds, NULL, NULL, &tv);
1780
1781 if (nfds <= 0) {
1782 return;
1783 }
1784
1785 if (ipv6_listen_fd >= 0 && FD_ISSET(ipv6_listen_fd, &fds)) {
1786
1787 csock = accept(ipv6_listen_fd, (struct sockaddr *)&addr, &addrlen);
1788 if (csock < 0) {
1789 rfbLogPerror("check_ipv6_listen: accept");
1790 goto err1;
1791 }
1792 if (fcntl(csock, F_SETFL, O_NONBLOCK) < 0) {
1793 rfbLogPerror("check_ipv6_listen: fcntl");
1794 close(csock);
1795 goto err1;
1796 }
1797 if (setsockopt(csock, IPPROTO_TCP, TCP_NODELAY,
1798 (char *)&one, sizeof(one)) < 0) {
1799 rfbLogPerror("check_ipv6_listen: setsockopt");
1800 close(csock);
1801 goto err1;
1802 }
1803
1804 name = ipv6_getipaddr((struct sockaddr *) &addr, addrlen);
1805
1806 ipv6_client_ip_str = name;
1807 cl = rfbNewClient(screen, csock);
1808 ipv6_client_ip_str = NULL;
1809 if (cl == NULL) {
1810 close(csock);
1811 goto err1;
1812 }
1813
1814 if (name) {
1815 if (cl->host) {
1816 free(cl->host);
1817 }
1818 cl->host = name;
1819 rfbLog("ipv6 client: %s\n", name);
1820 }
1821 }
1822
1823 err1:
1824
1825 if (ipv6_http_fd >= 0 && FD_ISSET(ipv6_http_fd, &fds)) {
1826
1827 csock = accept(ipv6_http_fd, (struct sockaddr *)&addr, &addrlen);
1828 if (csock < 0) {
1829 rfbLogPerror("check_ipv6_listen: accept");
1830 return;
1831 }
1832 if (fcntl(csock, F_SETFL, O_NONBLOCK) < 0) {
1833 rfbLogPerror("check_ipv6_listen: fcntl");
1834 close(csock);
1835 return;
1836 }
1837 if (setsockopt(csock, IPPROTO_TCP, TCP_NODELAY,
1838 (char *)&one, sizeof(one)) < 0) {
1839 rfbLogPerror("check_ipv6_listen: setsockopt");
1840 close(csock);
1841 return;
1842 }
1843
1844 rfbLog("check_ipv6_listen: setting httpSock to %d\n", csock);
1845 screen->httpSock = csock;
1846
1847 if (screen->httpListenSock < 0) {
1848 /* this may not always work... */
1849 int save = screen->httpListenSock;
1850 screen->httpListenSock = ipv6_http_fd;
1851 rfbLog("check_ipv6_listen: no httpListenSock, calling rfbHttpCheckFds()\n");
1852 rfbHttpCheckFds(screen);
1853 screen->httpListenSock = save;
1854 }
1855 }
1856 #endif
1857 if (usec) {}
1858 }
1859
check_unix_sock(long usec)1860 void check_unix_sock(long usec) {
1861 fd_set fds;
1862 struct timeval tv;
1863 int nfds, csock = -1;
1864 rfbClientPtr cl;
1865 int nmax = 0;
1866 char *name;
1867
1868 if (!unix_sock || unix_sock_fd < 0) {
1869 return;
1870 }
1871
1872 FD_ZERO(&fds);
1873 if (unix_sock_fd >= 0) {
1874 FD_SET(unix_sock_fd, &fds);
1875 nmax = unix_sock_fd;
1876 }
1877
1878 tv.tv_sec = 0;
1879 tv.tv_usec = 0;
1880
1881 nfds = select(nmax+1, &fds, NULL, NULL, &tv);
1882
1883 if (nfds <= 0) {
1884 return;
1885 }
1886
1887 if (unix_sock_fd >= 0 && FD_ISSET(unix_sock_fd, &fds)) {
1888 csock = accept_unix(unix_sock_fd);
1889 if (csock < 0) {
1890 return;
1891 }
1892 if (fcntl(csock, F_SETFL, O_NONBLOCK) < 0) {
1893 rfbLogPerror("check_unix_sock: fcntl");
1894 close(csock);
1895 return;
1896 }
1897
1898 /* rfbNewClient() will screw us with setsockopt TCP_NODELAY...
1899 you need to comment out in libvncserver/rfbserver.c:
1900 rfbLogPerror("setsockopt failed");
1901 close(sock);
1902 return NULL;
1903 */
1904 cl = rfbNewClient(screen, csock);
1905
1906 if (cl == NULL) {
1907 close(csock);
1908 return;
1909 }
1910
1911 name = strdup(unix_sock);
1912
1913 if (name) {
1914 if (cl->host) {
1915 free(cl->host);
1916 }
1917 cl->host = name;
1918 rfbLog("unix sock client: %s\n", name);
1919 }
1920 }
1921 }
1922
1923 /*
1924 * For the -connect <file> option: periodically read the file looking for
1925 * a connect string. If one is found set client_connect to it.
1926 */
check_connect_file(char * file)1927 static void check_connect_file(char *file) {
1928 FILE *in;
1929 char line[VNC_CONNECT_MAX], host[VNC_CONNECT_MAX];
1930 static int first_warn = 1, truncate_ok = 1;
1931 static double last_time = 0.0, delay = 0.5;
1932 double now = dnow();
1933 struct stat sbuf;
1934
1935 if (last_time == 0.0) {
1936 if (!getenv("X11VNC_APPSHARE_ACTIVE")) {
1937 /* skip first */
1938 last_time = now;
1939 } else {
1940 delay = 0.25;
1941 }
1942 }
1943 if (now - last_time < delay) {
1944 /* check only about once a second */
1945 return;
1946 }
1947 last_time = now;
1948
1949 if (! truncate_ok) {
1950 /* check if permissions changed */
1951 if (access(file, W_OK) == 0) {
1952 truncate_ok = 1;
1953 } else {
1954 return;
1955 }
1956 }
1957
1958 if (stat(file, &sbuf) == 0) {
1959 /* skip empty file directly */
1960 if (sbuf.st_size == 0) {
1961 return;
1962 }
1963 }
1964
1965 in = fopen(file, "r");
1966 if (in == NULL) {
1967 if (first_warn) {
1968 rfbLog("check_connect_file: fopen failure: %s\n", file);
1969 rfbLogPerror("fopen");
1970 first_warn = 0;
1971 }
1972 return;
1973 }
1974
1975 if (fgets(line, VNC_CONNECT_MAX, in) != NULL) {
1976 if (sscanf(line, "%s", host) == 1) {
1977 if (strlen(host) > 0) {
1978 char *str = strdup(host);
1979 if (strlen(str) > 38) {
1980 char trim[100];
1981 trim[0] = '\0';
1982 strncat(trim, str, 38);
1983 rfbLog("read connect file: %s ...\n",
1984 trim);
1985 } else {
1986 rfbLog("read connect file: %s\n", str);
1987 }
1988 if (!strcmp(str, "cmd=stop") &&
1989 dnowx() < 3.0) {
1990 rfbLog("ignoring stale cmd=stop\n");
1991 } else {
1992 client_connect = str;
1993 }
1994 }
1995 }
1996 }
1997 fclose(in);
1998
1999 /* truncate file */
2000 in = fopen(file, "w");
2001 if (in != NULL) {
2002 fclose(in);
2003 } else {
2004 /* disable if we cannot truncate */
2005 rfbLog("check_connect_file: could not truncate %s, "
2006 "disabling checking.\n", file);
2007 truncate_ok = 0;
2008 }
2009 }
2010
socks5_proxy(char * host,int port,int sock)2011 static int socks5_proxy(char *host, int port, int sock) {
2012 unsigned char buf[512], tmp[2];
2013 char reply[512];
2014 int len, n, i, j = 0;
2015
2016 memset(buf, 0, 512);
2017 memset(reply, 0, 512);
2018
2019 buf[0] = 0x5;
2020 buf[1] = 0x1;
2021 buf[2] = 0x0;
2022
2023 write(sock, buf, 3);
2024
2025 n = read(sock, buf, 2);
2026
2027 if (n != 2) {
2028 rfbLog("socks5_proxy: read error: %d\n", n);
2029 close(sock);
2030 return 0;
2031 }
2032 if (buf[0] != 0x5 || buf[1] != 0x0) {
2033 rfbLog("socks5_proxy: handshake error: %d %d\n", (int) buf[0], (int) buf[1]);
2034 close(sock);
2035 return 0;
2036 }
2037
2038 buf[0] = 0x5;
2039 buf[1] = 0x1;
2040 buf[2] = 0x0;
2041 buf[3] = 0x3;
2042
2043 buf[4] = (unsigned char) strlen(host);
2044 strcat((char *) buf+5, host);
2045
2046 len = 5 + strlen(host);
2047
2048 buf[len] = (unsigned char) (port >> 8);
2049 buf[len+1] = (unsigned char) (port & 0xff);
2050
2051 write(sock, buf, len+2);
2052
2053 for (i=0; i<4; i++) {
2054 int n;
2055 n = read(sock, tmp, 1);
2056 j++;
2057 if (n < 0) {
2058 if (errno != EINTR) {
2059 break;
2060 } else {
2061 i--;
2062 if (j > 100) {
2063 break;
2064 }
2065 continue;
2066 }
2067 }
2068 if (n == 0) {
2069 break;
2070 }
2071 reply[i] = tmp[0];
2072 }
2073 if (reply[3] == 0x1) {
2074 read(sock, reply+4, 4 + 2);
2075 } else if (reply[3] == 0x3) {
2076 n = read(sock, tmp, 1);
2077 reply[4] = tmp[0];
2078 read(sock, reply+5, (int) reply[4] + 2);
2079 } else if (reply[3] == 0x4) {
2080 read(sock, reply+4, 16 + 2);
2081 }
2082
2083 if (0) {
2084 int i;
2085 for (i=0; i<len+2; i++) {
2086 fprintf(stderr, "b[%d]: %d\n", i, (int) buf[i]);
2087 }
2088 for (i=0; i<len+2; i++) {
2089 fprintf(stderr, "r[%d]: %d\n", i, (int) reply[i]);
2090 }
2091 }
2092 if (reply[0] == 0x5 && reply[1] == 0x0 && reply[2] == 0x0) {
2093 rfbLog("SOCKS5 connect OK to %s:%d sock=%d\n", host, port, sock);
2094 return 1;
2095 } else {
2096 rfbLog("SOCKS5 error to %s:%d sock=%d\n", host, port, sock);
2097 close(sock);
2098 return 0;
2099 }
2100 }
2101
socks_proxy(char * host,int port,int sock)2102 static int socks_proxy(char *host, int port, int sock) {
2103 unsigned char buf[512], tmp[2];
2104 char reply[16];
2105 int socks4a = 0, len, i, j = 0, d1, d2, d3, d4;
2106
2107 memset(buf, 0, 512);
2108
2109 buf[0] = 0x4;
2110 buf[1] = 0x1;
2111 buf[2] = (unsigned char) (port >> 8);
2112 buf[3] = (unsigned char) (port & 0xff);
2113
2114
2115 if (strlen(host) > 256) {
2116 rfbLog("socks_proxy: hostname too long: %s\n", host);
2117 close(sock);
2118 return 0;
2119 }
2120
2121 if (!strcmp(host, "localhost") || !strcmp(host, "127.0.0.1")) {
2122 buf[4] = 127;
2123 buf[5] = 0;
2124 buf[6] = 0;
2125 buf[7] = 1;
2126 } else if (sscanf(host, "%d.%d.%d.%d", &d1, &d2, &d3, &d4) == 4) {
2127 buf[4] = (unsigned char) d1;
2128 buf[5] = (unsigned char) d2;
2129 buf[6] = (unsigned char) d3;
2130 buf[7] = (unsigned char) d4;
2131 } else {
2132 buf[4] = 0x0;
2133 buf[5] = 0x0;
2134 buf[6] = 0x0;
2135 buf[7] = 0x3;
2136 socks4a = 1;
2137 }
2138 len = 8;
2139
2140 strcat((char *)buf+8, "nobody");
2141 len += strlen("nobody") + 1;
2142
2143 if (socks4a) {
2144 strcat((char *) buf+8+strlen("nobody") + 1, host);
2145 len += strlen(host) + 1;
2146 }
2147
2148 write(sock, buf, len);
2149
2150 for (i=0; i<8; i++) {
2151 int n;
2152 n = read(sock, tmp, 1);
2153 j++;
2154 if (n < 0) {
2155 if (errno != EINTR) {
2156 break;
2157 } else {
2158 i--;
2159 if (j > 100) {
2160 break;
2161 }
2162 continue;
2163 }
2164 }
2165 if (n == 0) {
2166 break;
2167 }
2168 reply[i] = tmp[0];
2169 }
2170 if (0) {
2171 int i;
2172 for (i=0; i<len; i++) {
2173 fprintf(stderr, "b[%d]: %d\n", i, (int) buf[i]);
2174 }
2175 for (i=0; i<8; i++) {
2176 fprintf(stderr, "r[%d]: %d\n", i, (int) reply[i]);
2177 }
2178 }
2179 if (reply[0] == 0x0 && reply[1] == 0x5a) {
2180 if (socks4a) {
2181 rfbLog("SOCKS4a connect OK to %s:%d sock=%d\n", host, port, sock);
2182 } else {
2183 rfbLog("SOCKS4 connect OK to %s:%d sock=%d\n", host, port, sock);
2184 }
2185 return 1;
2186 } else {
2187 if (socks4a) {
2188 rfbLog("SOCKS4a error to %s:%d sock=%d\n", host, port, sock);
2189 } else {
2190 rfbLog("SOCKS4 error to %s:%d sock=%d\n", host, port, sock);
2191 }
2192 close(sock);
2193 return 0;
2194 }
2195 }
2196
2197 #define PXY_HTTP 1
2198 #define PXY_GET 2
2199 #define PXY_SOCKS 3
2200 #define PXY_SOCKS5 4
2201 #define PXY_SSH 5
2202 #define PXY 3
2203
2204 static int pxy_get_sock;
2205
pconnect(int psock,char * host,int port,int type,char * http_path,char * gethost,int getport)2206 static int pconnect(int psock, char *host, int port, int type, char *http_path, char *gethost, int getport) {
2207 char reply[4096];
2208 int i, ok, len;
2209 char *req;
2210
2211 pxy_get_sock = -1;
2212
2213 if (type == PXY_SOCKS) {
2214 return socks_proxy(host, port, psock);
2215 }
2216 if (type == PXY_SOCKS5) {
2217 return socks5_proxy(host, port, psock);
2218 }
2219 if (type == PXY_SSH) {
2220 return 1;
2221 }
2222
2223 len = strlen("CONNECT ") + strlen(host);
2224 if (type == PXY_GET) {
2225 len += strlen(http_path) + strlen(gethost);
2226 len += strlen("host=") + 1 + strlen("port=") + 1 + 1;
2227 }
2228 len += 1 + 20 + strlen("HTTP/1.1\r\n") + 1;
2229
2230 req = (char *)malloc(len);
2231
2232 if (type == PXY_GET) {
2233 int noquery = 0;
2234 char *t = strstr(http_path, "__END__");
2235 if (t) {
2236 noquery = 1;
2237 *t = '\0';
2238 }
2239
2240 if (noquery) {
2241 sprintf(req, "GET %s HTTP/1.1\r\n", http_path);
2242 } else {
2243 sprintf(req, "GET %shost=%s&port=%d HTTP/1.1\r\n", http_path, host, port);
2244 }
2245 } else {
2246 sprintf(req, "CONNECT %s:%d HTTP/1.1\r\n", host, port);
2247 }
2248 rfbLog("http proxy: %s", req);
2249 write(psock, req, strlen(req));
2250
2251 if (type == PXY_GET) {
2252 char *t = "Connection: close\r\n";
2253 write(psock, t, strlen(t));
2254 }
2255
2256 if (type == PXY_GET) {
2257 sprintf(req, "Host: %s:%d\r\n", gethost, getport);
2258 rfbLog("http proxy: %s", req);
2259 sprintf(req, "Host: %s:%d\r\n\r\n", gethost, getport);
2260 } else {
2261 sprintf(req, "Host: %s:%d\r\n", host, port);
2262 rfbLog("http proxy: %s", req);
2263 sprintf(req, "Host: %s:%d\r\n\r\n", host, port);
2264 }
2265
2266 write(psock, req, strlen(req));
2267
2268 ok = 0;
2269 reply[0] = '\0';
2270
2271 for (i=0; i<4096; i++) {
2272 int n;
2273 req[0] = req[1] = '\0';
2274 n = read(psock, req, 1);
2275 if (n < 0) {
2276 if (errno != EINTR) {
2277 break;
2278 } else {
2279 continue;
2280 }
2281 }
2282 if (n == 0) {
2283 break;
2284 }
2285 strcat(reply, req);
2286 if (strstr(reply, "\r\n\r\n")) {
2287 if (strstr(reply, "HTTP/") == reply) {
2288 char *q = strchr(reply, ' ');
2289 if (q) {
2290 q++;
2291 if (q[0] == '2' && q[1] == '0' && q[2] == '0' && q[3] == ' ') {
2292 ok = 1;
2293 }
2294 }
2295 }
2296 break;
2297 }
2298 }
2299
2300 if (type == PXY_GET) {
2301 char *t1 = strstr(reply, "VNC-IP-Port: ");
2302 char *t2 = strstr(reply, "VNC-Host-Port: ");
2303 char *s, *newhost = NULL;
2304 int newport = 0;
2305 fprintf(stderr, "%s\n", reply);
2306 if (t1) {
2307 t1 += strlen("VNC-IP-Port: ");
2308 s = strstr(t1, ":");
2309 if (s) {
2310 *s = '\0';
2311 newhost = strdup(t1);
2312 newport = atoi(s+1);
2313 }
2314 } else if (t2) {
2315 t2 += strlen("VNC-Host-Port: ");
2316 s = strstr(t2, ":");
2317 if (s) {
2318 *s = '\0';
2319 newhost = strdup(t2);
2320 newport = atoi(s+1);
2321 }
2322 }
2323 if (newhost && newport > 0) {
2324 rfbLog("proxy GET reconnect to: %s:%d\n", newhost, newport);
2325 pxy_get_sock = connect_tcp(newhost, newport);
2326 }
2327 }
2328 free(req);
2329
2330 return ok;
2331 }
2332
proxy_connect(char * host,int port)2333 static int proxy_connect(char *host, int port) {
2334 char *p, *q, *str;
2335 int i, n, pxy[PXY],pxy_p[PXY];
2336 int psock = -1;
2337 char *pxy_h[PXY], *pxy_g[PXY];
2338
2339 if (! connect_proxy) {
2340 return -1;
2341 }
2342 str = strdup(connect_proxy);
2343
2344 for (i=0; i<PXY; i++) {
2345 pxy[i] = 0;
2346 pxy_p[i] = 0;
2347 pxy_h[i] = NULL;
2348 pxy_g[i] = NULL;
2349 }
2350
2351 n = 0;
2352 p = str;
2353 while (p) {
2354 char *hp, *c, *s = NULL;
2355
2356 q = strchr(p, ',');
2357 if (q) {
2358 *q = '\0';
2359 }
2360
2361 if (n==0) fprintf(stderr, "\n");
2362 rfbLog("proxy_connect[%d]: %s\n", n+1, p);
2363
2364 pxy[n] = 0;
2365 pxy_p[n] = 0;
2366 pxy_h[n] = NULL;
2367 pxy_g[n] = NULL;
2368
2369 if (strstr(p, "socks://") == p) {
2370 hp = strstr(p, "://") + 3;
2371 pxy[n] = PXY_SOCKS;
2372 } else if (strstr(p, "socks4://") == p) {
2373 hp = strstr(p, "://") + 3;
2374 pxy[n] = PXY_SOCKS;
2375 } else if (strstr(p, "socks5://") == p) {
2376 hp = strstr(p, "://") + 3;
2377 pxy[n] = PXY_SOCKS5;
2378 } else if (strstr(p, "ssh://") == p) {
2379 if (n != 0) {
2380 rfbLog("ssh:// proxy must be the first one\n");
2381 clean_up_exit(1);
2382 }
2383 hp = strstr(p, "://") + 3;
2384 pxy[n] = PXY_SSH;
2385 } else if (strstr(p, "http://") == p) {
2386 hp = strstr(p, "://") + 3;
2387 pxy[n] = PXY_HTTP;
2388 } else if (strstr(p, "https://") == p) {
2389 hp = strstr(p, "://") + 3;
2390 pxy[n] = PXY_HTTP;
2391 } else {
2392 hp = p;
2393 pxy[n] = PXY_HTTP;
2394 }
2395 c = strstr(hp, ":");
2396 if (!c && pxy[n] == PXY_SSH) {
2397 char *hp2 = (char *) malloc(strlen(hp) + 5);
2398 sprintf(hp2, "%s:1", hp);
2399 hp = hp2;
2400 c = strstr(hp, ":");
2401 }
2402 if (!c) {
2403 pxy[n] = 0;
2404 if (q) {
2405 *q = ',';
2406 p = q + 1;
2407 } else {
2408 p = NULL;
2409 }
2410 continue;
2411 }
2412
2413 if (pxy[n] == PXY_HTTP) {
2414 s = strstr(c, "/");
2415 if (s) {
2416 pxy[n] = PXY_GET;
2417 pxy_g[n] = strdup(s);
2418 *s = '\0';
2419 }
2420 }
2421 pxy_p[n] = atoi(c+1);
2422
2423 if (pxy_p[n] <= 0) {
2424 pxy[n] = 0;
2425 pxy_p[n] = 0;
2426 if (q) {
2427 *q = ',';
2428 p = q + 1;
2429 } else {
2430 p = NULL;
2431 }
2432 continue;
2433 }
2434 *c = '\0';
2435 pxy_h[n] = strdup(hp);
2436
2437 if (++n >= PXY) {
2438 break;
2439 }
2440
2441 if (q) {
2442 *q = ',';
2443 p = q + 1;
2444 } else {
2445 p = NULL;
2446 }
2447 }
2448 free(str);
2449
2450 if (!n) {
2451 psock = -1;
2452 goto pxy_clean;
2453 }
2454
2455 if (pxy[0] == PXY_SSH) {
2456 int rc, len = 0;
2457 char *cmd, *ssh;
2458 int sport = find_free_port(7300, 8000);
2459 if (getenv("SSH")) {
2460 ssh = getenv("SSH");
2461 } else {
2462 ssh = "ssh";
2463 }
2464 len = 200 + strlen(ssh) + strlen(pxy_h[0]) + strlen(host);
2465 cmd = (char *) malloc(len);
2466 if (n == 1) {
2467 if (pxy_p[0] <= 1) {
2468 sprintf(cmd, "%s -f -L '%d:%s:%d' '%s' 'sleep 20'", ssh, sport, host, port, pxy_h[0]);
2469 } else {
2470 sprintf(cmd, "%s -f -p %d -L '%d:%s:%d' '%s' 'sleep 20'", ssh, pxy_p[0], sport, host, port, pxy_h[0]);
2471 }
2472 } else {
2473 if (pxy_p[0] <= 1) {
2474 sprintf(cmd, "%s -f -L '%d:%s:%d' '%s' 'sleep 20'", ssh, sport, pxy_h[1], pxy_p[1], pxy_h[0]);
2475 } else {
2476 sprintf(cmd, "%s -f -p %d -L '%d:%s:%d' '%s' 'sleep 20'", ssh, pxy_p[0], sport, pxy_h[1], pxy_p[1], pxy_h[0]);
2477 }
2478 }
2479 if (no_external_cmds || !cmd_ok("ssh")) {
2480 rfbLogEnable(1);
2481 rfbLog("cannot run external commands in -nocmds mode:\n");
2482 rfbLog(" \"%s\"\n", cmd);
2483 rfbLog(" exiting.\n");
2484 clean_up_exit(1);
2485 }
2486 close_exec_fds();
2487 fprintf(stderr, "\n");
2488 rfbLog("running: %s\n", cmd);
2489 rc = system(cmd);
2490 free(cmd);
2491 if (rc != 0) {
2492 psock = -1;
2493 goto pxy_clean;
2494 }
2495 psock = connect_tcp("localhost", sport);
2496
2497 } else {
2498 psock = connect_tcp(pxy_h[0], pxy_p[0]);
2499 }
2500
2501 if (psock < 0) {
2502 psock = -1;
2503 goto pxy_clean;
2504 }
2505 rfbLog("opened socket to proxy: %s:%d\n", pxy_h[0], pxy_p[0]);
2506
2507 if (n >= 2) {
2508 if (! pconnect(psock, pxy_h[1], pxy_p[1], pxy[0], pxy_g[0], pxy_h[0], pxy_p[0])) {
2509 close(psock); psock = -1; goto pxy_clean;
2510 }
2511 if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
2512
2513 if (n >= 3) {
2514 if (! pconnect(psock, pxy_h[2], pxy_p[2], pxy[1], pxy_g[1], pxy_h[1], pxy_p[1])) {
2515 close(psock); psock = -1; goto pxy_clean;
2516 }
2517 if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
2518 if (! pconnect(psock, host, port, pxy[2], pxy_g[2], pxy_h[2], pxy_p[2])) {
2519 close(psock); psock = -1; goto pxy_clean;
2520 }
2521 if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
2522
2523 } else {
2524 if (! pconnect(psock, host, port, pxy[1], pxy_g[1], pxy_h[1], pxy_p[1])) {
2525 close(psock); psock = -1; goto pxy_clean;
2526 }
2527 if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
2528 }
2529 } else {
2530 if (! pconnect(psock, host, port, pxy[0], pxy_g[0], pxy_h[0], pxy_p[0])) {
2531 close(psock); psock = -1; goto pxy_clean;
2532 }
2533 if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
2534 }
2535
2536 pxy_clean:
2537 for (i=0; i < PXY; i++) {
2538 if (pxy_h[i] != NULL) {
2539 free(pxy_h[i]);
2540 }
2541 if (pxy_g[i] != NULL) {
2542 free(pxy_g[i]);
2543 }
2544 }
2545
2546 return psock;
2547 }
2548
get_repeater_string(char * str,int * len)2549 char *get_repeater_string(char *str, int *len) {
2550 int pren, which = 0;
2551 int prestring_len = 0;
2552 char *prestring = NULL, *ptmp = NULL;
2553 char *equals = strchr(str, '=');
2554 char *plus = strrchr(str, '+');
2555
2556 *len = 0;
2557 if (!plus || !equals) {
2558 return NULL;
2559 }
2560
2561 *plus = '\0';
2562 if (strstr(str, "repeater=") == str) {
2563 /* ultravnc repeater http://www.uvnc.com/addons/repeater.html */
2564 prestring_len = 250;
2565 ptmp = (char *) calloc(prestring_len+1, 1);
2566 snprintf(ptmp, prestring_len, "%s", str + strlen("repeater="));
2567 which = 1;
2568 } else if (strstr(str, "pre=") == str) {
2569 prestring_len = strlen(str + strlen("pre="));
2570 ptmp = (char *) calloc(prestring_len+1, 1);
2571 snprintf(ptmp, prestring_len+1, "%s", str + strlen("pre="));
2572 which = 2;
2573 } else if (sscanf(str, "pre%d=", &pren) == 1) {
2574 if (pren > 0 && pren <= 16384) {
2575 prestring_len = pren;
2576 ptmp = (char *) calloc(prestring_len+1, 1);
2577 snprintf(prestring, prestring_len, "%s", equals+1);
2578 which = 3;
2579 }
2580 }
2581 if (ptmp != NULL) {
2582 int i, k = 0;
2583 char *p = ptmp;
2584 prestring = (char *)calloc(prestring_len+1, 1);
2585 /* translate \n to newline, etc. */
2586 for (i=0; i < prestring_len; i++) {
2587 if (i < prestring_len-1 && *(p+i) == '\\') {
2588 if (*(p+i+1) == 'r') {
2589 prestring[k++] = '\r'; i++;
2590 } else if (*(p+i+1) == 'n') {
2591 prestring[k++] = '\n'; i++;
2592 } else if (*(p+i+1) == 't') {
2593 prestring[k++] = '\t'; i++;
2594 } else if (*(p+i+1) == 'a') {
2595 prestring[k++] = '\a'; i++;
2596 } else if (*(p+i+1) == 'b') {
2597 prestring[k++] = '\b'; i++;
2598 } else if (*(p+i+1) == 'v') {
2599 prestring[k++] = '\v'; i++;
2600 } else if (*(p+i+1) == 'f') {
2601 prestring[k++] = '\f'; i++;
2602 } else if (*(p+i+1) == '\\') {
2603 prestring[k++] = '\\'; i++;
2604 } else if (*(p+i+1) == 'c') {
2605 prestring[k++] = ','; i++;
2606 } else {
2607 prestring[k++] = *(p+i);
2608 }
2609 } else {
2610 prestring[k++] = *(p+i);
2611 }
2612 }
2613 if (which == 2) {
2614 prestring_len = k;
2615 }
2616 if (!quiet) {
2617 rfbLog("-connect prestring: '%s'\n", prestring);
2618 }
2619 free(ptmp);
2620 }
2621 *plus = '+';
2622
2623 *len = prestring_len;
2624 return prestring;
2625 }
2626
2627 #ifndef USE_TIMEOUT_INTERRUPT
2628 #define USE_TIMEOUT_INTERRUPT 0
2629 #endif
2630
reverse_connect_timeout(int sig)2631 static void reverse_connect_timeout (int sig) {
2632 rfbLog("sig: %d, reverse_connect_timeout.\n", sig);
2633 #if USE_TIMEOUT_INTERRUPT
2634 rfbLog("reverse_connect_timeout proceeding assuming connect(2) interrupt.\n");
2635 #else
2636 clean_up_exit(0);
2637 #endif
2638 }
2639
2640
2641 /*
2642 * Do a reverse connect for a single "host" or "host:port"
2643 */
2644
do_reverse_connect(char * str_in)2645 static int do_reverse_connect(char *str_in) {
2646 rfbClientPtr cl;
2647 char *host, *p, *str = str_in;
2648 char *prestring = NULL;
2649 int prestring_len = 0;
2650 int rport = 5500, len = strlen(str);
2651 int set_alarm = 0;
2652
2653 if (len < 1) {
2654 return 0;
2655 }
2656 if (len > 1024) {
2657 rfbLog("reverse_connect: string too long: %d bytes\n", len);
2658 return 0;
2659 }
2660 if (!screen) {
2661 rfbLog("reverse_connect: screen not setup yet.\n");
2662 return 0;
2663 }
2664 if (unixpw_in_progress) return 0;
2665
2666 /* look for repeater pre-string */
2667 if (strchr(str, '=') && strrchr(str, '+')
2668 && (strstr(str, "pre") == str || strstr(str, "repeater=") == str)) {
2669 prestring = get_repeater_string(str, &prestring_len);
2670 str = strrchr(str, '+') + 1;
2671 } else if (strrchr(str, '+') && strstr(str, "repeater://") == str) {
2672 /* repeater://host:port+string */
2673 /* repeater=string+host:port */
2674 char *plus = strrchr(str, '+');
2675 str = (char *) malloc(strlen(str_in)+1);
2676 *plus = '\0';
2677 sprintf(str, "repeater=%s+%s", plus+1, str_in + strlen("repeater://"));
2678 prestring = get_repeater_string(str, &prestring_len);
2679 str = strrchr(str, '+') + 1;
2680 *plus = '+';
2681 }
2682
2683 /* copy in to host */
2684 host = (char *) malloc(len+1);
2685 if (! host) {
2686 rfbLog("reverse_connect: could not malloc string %d\n", len);
2687 return 0;
2688 }
2689 strncpy(host, str, len);
2690 host[len] = '\0';
2691
2692 /* extract port, if any */
2693 if ((p = strrchr(host, ':')) != NULL) {
2694 rport = atoi(p+1);
2695 if (rport < 0) {
2696 rport = -rport;
2697 } else if (rport < 20) {
2698 rport = 5500 + rport;
2699 }
2700 *p = '\0';
2701 }
2702
2703 if (ipv6_client_ip_str) {
2704 free(ipv6_client_ip_str);
2705 ipv6_client_ip_str = NULL;
2706 }
2707
2708 if (use_openssl) {
2709 int vncsock;
2710 if (connect_proxy) {
2711 vncsock = proxy_connect(host, rport);
2712 } else {
2713 vncsock = connect_tcp(host, rport);
2714 }
2715 if (vncsock < 0) {
2716 rfbLog("reverse_connect: failed to connect to: %s\n", str);
2717 return 0;
2718 }
2719 if (prestring != NULL) {
2720 write(vncsock, prestring, prestring_len);
2721 free(prestring);
2722 }
2723 /* XXX use header */
2724 #define OPENSSL_REVERSE 6
2725 if (!getenv("X11VNC_DISABLE_SSL_CLIENT_MODE")) {
2726 openssl_init(1);
2727 }
2728
2729 if (first_conn_timeout > 0) {
2730 set_alarm = 1;
2731 signal(SIGALRM, reverse_connect_timeout);
2732 #if USE_TIMEOUT_INTERRUPT
2733 siginterrupt(SIGALRM, 1);
2734 #endif
2735 rfbLog("reverse_connect: using alarm() timeout of %d seconds.\n", first_conn_timeout);
2736 alarm(first_conn_timeout);
2737 }
2738 accept_openssl(OPENSSL_REVERSE, vncsock);
2739 if (set_alarm) {alarm(0); signal(SIGALRM, SIG_DFL);}
2740
2741 openssl_init(0);
2742 free(host);
2743 return 1;
2744 }
2745
2746 if (use_stunnel) {
2747 if(strcmp(host, "localhost") && strcmp(host, "127.0.0.1")) {
2748 if (!getenv("STUNNEL_DISABLE_LOCALHOST")) {
2749 rfbLog("reverse_connect: error host not localhost in -stunnel mode.\n");
2750 free(host);
2751 return 0;
2752 }
2753 }
2754 }
2755
2756 if (unixpw) {
2757 int is_localhost = 0, user_disabled_it = 0;
2758
2759 if(!strcmp(host, "localhost") || !strcmp(host, "127.0.0.1")) {
2760 is_localhost = 1;
2761 }
2762 if (getenv("UNIXPW_DISABLE_LOCALHOST")) {
2763 user_disabled_it = 1;
2764 }
2765
2766 if (! is_localhost) {
2767 if (user_disabled_it) {
2768 rfbLog("reverse_connect: warning disabling localhost constraint in -unixpw\n");
2769 } else {
2770 rfbLog("reverse_connect: error not localhost in -unixpw\n");
2771 free(host);
2772 return 0;
2773 }
2774 }
2775 }
2776
2777 if (first_conn_timeout > 0) {
2778 set_alarm = 1;
2779 signal(SIGALRM, reverse_connect_timeout);
2780 #if USE_TIMEOUT_INTERRUPT
2781 siginterrupt(SIGALRM, 1);
2782 #endif
2783 rfbLog("reverse_connect: using alarm() timeout of %d seconds.\n", first_conn_timeout);
2784 alarm(first_conn_timeout);
2785 }
2786
2787 if (connect_proxy != NULL) {
2788 int sock = proxy_connect(host, rport);
2789 if (set_alarm) {alarm(0); signal(SIGALRM, SIG_DFL);}
2790 if (sock >= 0) {
2791 if (prestring != NULL) {
2792 write(sock, prestring, prestring_len);
2793 free(prestring);
2794 }
2795 cl = create_new_client(sock, 1);
2796 } else {
2797 return 0;
2798 }
2799 } else if (prestring != NULL) {
2800 int sock = connect_tcp(host, rport);
2801 if (set_alarm) {alarm(0); signal(SIGALRM, SIG_DFL);}
2802 if (sock >= 0) {
2803 write(sock, prestring, prestring_len);
2804 free(prestring);
2805 cl = create_new_client(sock, 1);
2806 } else {
2807 return 0;
2808 }
2809 } else {
2810 cl = rfbReverseConnection(screen, host, rport);
2811 if (cl == NULL) {
2812 int sock = connect_tcp(host, rport);
2813 if (sock >= 0) {
2814 cl = create_new_client(sock, 1);
2815 }
2816 }
2817 if (set_alarm) {alarm(0); signal(SIGALRM, SIG_DFL);}
2818 if (cl != NULL && use_threads) {
2819 cl->onHold = FALSE;
2820 rfbStartOnHoldClient(cl);
2821 }
2822 }
2823
2824 free(host);
2825
2826 if (ipv6_client_ip_str) {
2827 free(ipv6_client_ip_str);
2828 ipv6_client_ip_str = NULL;
2829 }
2830
2831
2832 if (cl == NULL) {
2833 if (quiet && connect_or_exit) {
2834 rfbLogEnable(1);
2835 }
2836 rfbLog("reverse_connect: %s failed\n", str);
2837 return 0;
2838 } else {
2839 rfbLog("reverse_connect: %s/%s OK\n", str, cl->host);
2840 /* let's see if anyone complains: */
2841 if (! getenv("X11VNC_REVERSE_CONNECTION_NO_AUTH")) {
2842 rfbLog("reverse_connect: turning on auth for %s\n",
2843 cl->host);
2844 cl->reverseConnection = FALSE;
2845 }
2846 return 1;
2847 }
2848 }
2849
2850 /*
2851 * Break up comma separated list of hosts and call do_reverse_connect()
2852 */
reverse_connect(char * str)2853 void reverse_connect(char *str) {
2854 char *p, *tmp;
2855 int sleep_between_host = 300;
2856 int sleep_min = 1500, sleep_max = 4500, n_max = 5;
2857 int n, tot, t, dt = 100, cnt = 0;
2858 int nclients0 = client_count;
2859 int lcnt, j;
2860 char **list;
2861 int do_appshare = 0;
2862
2863 if (!getenv("X11VNC_REVERSE_USE_OLD_SLEEP")) {
2864 sleep_min = 500;
2865 sleep_max = 2500;
2866 }
2867
2868 if (unixpw_in_progress) return;
2869
2870 tmp = strdup(str);
2871
2872 list = (char **) calloc( (strlen(tmp)+2) * sizeof (char *), 1);
2873 lcnt = 0;
2874
2875 p = strtok(tmp, ", \t\r\n");
2876 while (p) {
2877 list[lcnt++] = strdup(p);
2878 p = strtok(NULL, ", \t\r\n");
2879 }
2880 free(tmp);
2881
2882 if (subwin && getenv("X11VNC_APPSHARE_ACTIVE")) {
2883 do_appshare = 1;
2884 sleep_between_host = 0; /* too agressive??? */
2885 }
2886 if (getenv("X11VNC_REVERSE_SLEEP_BETWEEN_HOST")) {
2887 sleep_between_host = atoi(getenv("X11VNC_REVERSE_SLEEP_BETWEEN_HOST"));
2888 }
2889
2890 if (do_appshare) {
2891 if (screen && dpy) {
2892 char *s = choose_title(DisplayString(dpy));
2893
2894 /* mutex */
2895 screen->desktopName = s;
2896 if (rfb_desktop_name) {
2897 free(rfb_desktop_name);
2898 }
2899 rfb_desktop_name = strdup(s);
2900 }
2901 }
2902
2903 for (j = 0; j < lcnt; j++) {
2904 p = list[j];
2905
2906 if ((n = do_reverse_connect(p)) != 0) {
2907 int i;
2908 progress_client();
2909 for (i=0; i < 3; i++) {
2910 rfbPE(-1);
2911 }
2912 }
2913 cnt += n;
2914 if (list[j+1] != NULL) {
2915 t = 0;
2916 while (t < sleep_between_host) {
2917 double t1, t2;
2918 int i;
2919 t1 = dnow();
2920 for (i=0; i < 8; i++) {
2921 rfbPE(-1);
2922 if (do_appshare && t == 0) {
2923 rfbPE(-1);
2924 }
2925 }
2926 t2 = dnow();
2927 t += (int) (1000 * (t2 - t1));
2928 if (t >= sleep_between_host) {
2929 break;
2930 }
2931 usleep(dt * 1000);
2932 t += dt;
2933 }
2934 }
2935 }
2936
2937 for (j = 0; j < lcnt; j++) {
2938 p = list[j];
2939 if (p) free(p);
2940 }
2941 free(list);
2942
2943 if (cnt == 0) {
2944 if (connect_or_exit) {
2945 rfbLogEnable(1);
2946 rfbLog("exiting under -connect_or_exit\n");
2947 if (gui_pid > 0) {
2948 rfbLog("killing gui_pid %d\n", gui_pid);
2949 kill(gui_pid, SIGTERM);
2950 }
2951 clean_up_exit(1);
2952 }
2953 if (xrandr || xrandr_maybe) {
2954 check_xrandr_event("reverse_connect1");
2955 }
2956 return;
2957 }
2958
2959 /*
2960 * XXX: we need to process some of the initial handshaking
2961 * events, otherwise the client can get messed up (why??)
2962 * so we send rfbProcessEvents() all over the place.
2963 *
2964 * How much is this still needed?
2965 */
2966
2967 n = cnt;
2968 if (n >= n_max) {
2969 n = n_max;
2970 }
2971 t = sleep_max - sleep_min;
2972 tot = sleep_min + ((n-1) * t) / (n_max-1);
2973
2974 if (do_appshare) {
2975 tot /= 3;
2976 if (tot < dt) {
2977 tot = dt;
2978 }
2979 tot = 0; /* too agressive??? */
2980 }
2981
2982 if (getenv("X11VNC_REVERSE_SLEEP_MAX")) {
2983 tot = atoi(getenv("X11VNC_REVERSE_SLEEP_MAX"));
2984 }
2985
2986 t = 0;
2987 while (t < tot) {
2988 int i;
2989 double t1, t2;
2990 t1 = dnow();
2991 for (i=0; i < 8; i++) {
2992 rfbPE(-1);
2993 if (t == 0) rfbPE(-1);
2994 }
2995 t2 = dnow();
2996 t += (int) (1000 * (t2 - t1));
2997 if (t >= tot) {
2998 break;
2999 }
3000 usleep(dt * 1000);
3001 t += dt;
3002 }
3003 if (connect_or_exit) {
3004 if (client_count <= nclients0) {
3005 for (t = 0; t < 10; t++) {
3006 int i;
3007 for (i=0; i < 3; i++) {
3008 rfbPE(-1);
3009 }
3010 usleep(100 * 1000);
3011 }
3012 }
3013 if (client_count <= nclients0) {
3014 rfbLogEnable(1);
3015 rfbLog("exiting under -connect_or_exit\n");
3016 if (gui_pid > 0) {
3017 rfbLog("killing gui_pid %d\n", gui_pid);
3018 kill(gui_pid, SIGTERM);
3019 }
3020 clean_up_exit(1);
3021 }
3022 }
3023 if (xrandr || xrandr_maybe) {
3024 check_xrandr_event("reverse_connect2");
3025 }
3026 }
3027
3028 /*
3029 * Routines for monitoring the VNC_CONNECT and X11VNC_REMOTE properties
3030 * for changes. The vncconnect(1) will set it on our X display.
3031 */
set_vnc_connect_prop(char * str)3032 void set_vnc_connect_prop(char *str) {
3033 RAWFB_RET_VOID
3034 #if !NO_X11
3035 if (vnc_connect_prop == None) return;
3036 XChangeProperty(dpy, rootwin, vnc_connect_prop, XA_STRING, 8,
3037 PropModeReplace, (unsigned char *)str, strlen(str));
3038 #else
3039 if (!str) {}
3040 #endif /* NO_X11 */
3041 }
3042
set_x11vnc_remote_prop(char * str)3043 void set_x11vnc_remote_prop(char *str) {
3044 RAWFB_RET_VOID
3045 #if !NO_X11
3046 if (x11vnc_remote_prop == None) return;
3047 XChangeProperty(dpy, rootwin, x11vnc_remote_prop, XA_STRING, 8,
3048 PropModeReplace, (unsigned char *)str, strlen(str));
3049 #else
3050 if (!str) {}
3051 #endif /* NO_X11 */
3052 }
3053
read_vnc_connect_prop(int nomsg)3054 void read_vnc_connect_prop(int nomsg) {
3055 #if NO_X11
3056 RAWFB_RET_VOID
3057 if (!nomsg) {}
3058 return;
3059 #else
3060 Atom type;
3061 int format, slen, dlen;
3062 unsigned long nitems = 0, bytes_after = 0;
3063 unsigned char* data = NULL;
3064 int db = 1;
3065
3066 vnc_connect_str[0] = '\0';
3067 slen = 0;
3068
3069 if (! vnc_connect || vnc_connect_prop == None) {
3070 /* not active or problem with VNC_CONNECT atom */
3071 return;
3072 }
3073 RAWFB_RET_VOID
3074
3075 /* read the property value into vnc_connect_str: */
3076 do {
3077 if (XGetWindowProperty(dpy, DefaultRootWindow(dpy),
3078 vnc_connect_prop, nitems/4, VNC_CONNECT_MAX/16, False,
3079 AnyPropertyType, &type, &format, &nitems, &bytes_after,
3080 &data) == Success) {
3081
3082 dlen = nitems * (format/8);
3083 if (slen + dlen > VNC_CONNECT_MAX) {
3084 /* too big */
3085 rfbLog("warning: truncating large VNC_CONNECT"
3086 " string > %d bytes.\n", VNC_CONNECT_MAX);
3087 XFree_wr(data);
3088 break;
3089 }
3090 memcpy(vnc_connect_str+slen, data, dlen);
3091 slen += dlen;
3092 vnc_connect_str[slen] = '\0';
3093 XFree_wr(data);
3094 }
3095 } while (bytes_after > 0);
3096
3097 vnc_connect_str[VNC_CONNECT_MAX] = '\0';
3098 if (! db || nomsg) {
3099 ;
3100 } else {
3101 rfbLog("read VNC_CONNECT: %s\n", vnc_connect_str);
3102 }
3103 #endif /* NO_X11 */
3104 }
3105
read_x11vnc_remote_prop(int nomsg)3106 void read_x11vnc_remote_prop(int nomsg) {
3107 #if NO_X11
3108 RAWFB_RET_VOID
3109 if (!nomsg) {}
3110 return;
3111 #else
3112 Atom type;
3113 int format, slen, dlen;
3114 unsigned long nitems = 0, bytes_after = 0;
3115 unsigned char* data = NULL;
3116 int db = 1;
3117
3118 x11vnc_remote_str[0] = '\0';
3119 slen = 0;
3120
3121 if (! vnc_connect || x11vnc_remote_prop == None) {
3122 /* not active or problem with X11VNC_REMOTE atom */
3123 return;
3124 }
3125 RAWFB_RET_VOID
3126
3127 /* read the property value into x11vnc_remote_str: */
3128 do {
3129 if (XGetWindowProperty(dpy, DefaultRootWindow(dpy),
3130 x11vnc_remote_prop, nitems/4, X11VNC_REMOTE_MAX/16, False,
3131 AnyPropertyType, &type, &format, &nitems, &bytes_after,
3132 &data) == Success) {
3133
3134 dlen = nitems * (format/8);
3135 if (slen + dlen > X11VNC_REMOTE_MAX) {
3136 /* too big */
3137 rfbLog("warning: truncating large X11VNC_REMOTE"
3138 " string > %d bytes.\n", X11VNC_REMOTE_MAX);
3139 XFree_wr(data);
3140 break;
3141 }
3142 memcpy(x11vnc_remote_str+slen, data, dlen);
3143 slen += dlen;
3144 x11vnc_remote_str[slen] = '\0';
3145 XFree_wr(data);
3146 }
3147 } while (bytes_after > 0);
3148
3149 x11vnc_remote_str[X11VNC_REMOTE_MAX] = '\0';
3150 if (! db || nomsg) {
3151 ;
3152 } else if (strstr(x11vnc_remote_str, "ans=stop:N/A,ans=quit:N/A,ans=")) {
3153 ;
3154 } else if (strstr(x11vnc_remote_str, "qry=stop,quit,exit")) {
3155 ;
3156 } else if (strstr(x11vnc_remote_str, "ack=") == x11vnc_remote_str) {
3157 ;
3158 } else if (quiet && strstr(x11vnc_remote_str, "qry=ping") ==
3159 x11vnc_remote_str) {
3160 ;
3161 } else if (strstr(x11vnc_remote_str, "cmd=") &&
3162 strstr(x11vnc_remote_str, "passwd")) {
3163 rfbLog("read X11VNC_REMOTE: *\n");
3164 } else if (strlen(x11vnc_remote_str) > 36) {
3165 char trim[100];
3166 trim[0] = '\0';
3167 strncat(trim, x11vnc_remote_str, 36);
3168 rfbLog("read X11VNC_REMOTE: %s ...\n", trim);
3169
3170 } else {
3171 rfbLog("read X11VNC_REMOTE: %s\n", x11vnc_remote_str);
3172 }
3173 #endif /* NO_X11 */
3174 }
3175
3176 extern int rc_npieces;
3177
grab_state(int * ptr_grabbed,int * kbd_grabbed)3178 void grab_state(int *ptr_grabbed, int *kbd_grabbed) {
3179 int rcp, rck;
3180 double t0, t1;
3181 double ta, tb, tc;
3182 *ptr_grabbed = -1;
3183 *kbd_grabbed = -1;
3184
3185 if (!dpy) {
3186 return;
3187 }
3188 *ptr_grabbed = 0;
3189 *kbd_grabbed = 0;
3190
3191 #if !NO_X11
3192 X_LOCK;
3193
3194 XSync(dpy, False);
3195
3196 ta = t0 = dnow();
3197
3198 rcp = XGrabPointer(dpy, window, False, 0, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
3199 XUngrabPointer(dpy, CurrentTime);
3200
3201 tb = dnow();
3202
3203 rck = XGrabKeyboard(dpy, window, False, GrabModeAsync, GrabModeAsync, CurrentTime);
3204 XUngrabKeyboard(dpy, CurrentTime);
3205
3206 tc = dnow();
3207
3208 XSync(dpy, False);
3209
3210 t1 = dnow();
3211
3212 X_UNLOCK;
3213 if (rcp == AlreadyGrabbed || rcp == GrabFrozen) {
3214 *ptr_grabbed = 1;
3215 }
3216 if (rck == AlreadyGrabbed || rck == GrabFrozen) {
3217 *kbd_grabbed = 1;
3218 }
3219 if (rc_npieces < 10) {
3220 rfbLog("grab_state: checked %d,%d in %.6f sec (%.6f %.6f)\n",
3221 *ptr_grabbed, *kbd_grabbed, t1-t0, tb-ta, tc-tb);
3222 }
3223 #endif
3224 }
3225
pmove(int x,int y)3226 static void pmove(int x, int y) {
3227 if (x < 0 || y < 0) {
3228 rfbLog("pmove: skipping negative x or y: %d %d\n", x, y);
3229 return;
3230 }
3231 rfbLog("pmove: x y: %d %d\n", x, y);
3232 pointer_event(0, x, y, NULL);
3233 X_LOCK;
3234 XFlush_wr(dpy);
3235 X_UNLOCK;
3236 }
3237
3238
bcx_xattach(char * str,int * pg_init,int * kg_init)3239 char *bcx_xattach(char *str, int *pg_init, int *kg_init) {
3240 int grab_check = 1;
3241 int shift = 20;
3242 int final_x = 30, final_y = 30;
3243 int extra_x = -1, extra_y = -1;
3244 int t1, t2, dt = 40 * 1000;
3245 int ifneeded = 0;
3246 char *dir = "none", *flip = "none", *q;
3247 int pg1, kg1, pg2, kg2;
3248 char _bcx_res[128];
3249
3250 /* str:[up,down,left,right]+nograbcheck+shift=n+final=x+y+extra_move=x+y+[master_to_slave,slave_to_master,M2S,S2M]+dt=n+retry=n+ifneeded */
3251
3252 if (strstr(str, "up")) {
3253 dir = "up";
3254 } else if (strstr(str, "down")) {
3255 dir = "down";
3256 } else if (strstr(str, "left")) {
3257 dir = "left";
3258 } else if (strstr(str, "right")) {
3259 dir = "right";
3260 } else {
3261 return strdup("FAIL,NO_DIRECTION_SPECIFIED");
3262 }
3263
3264 if (strstr(str, "master_to_slave") || strstr(str, "M2S")) {
3265 flip = "M2S";
3266 } else if (strstr(str, "slave_to_master") || strstr(str, "S2M")) {
3267 flip = "S2M";
3268 } else {
3269 return strdup("FAIL,NO_MODE_CHANGE_SPECIFIED");
3270 }
3271
3272 if (strstr(str, "nograbcheck")) {
3273 grab_check = 0;
3274 }
3275 if (strstr(str, "ifneeded")) {
3276 ifneeded = 1;
3277 }
3278 q = strstr(str, "shift=");
3279 if (q && sscanf(q, "shift=%d", &t1) == 1) {
3280 shift = t1;
3281 }
3282 q = strstr(str, "final=");
3283 if (q && sscanf(q, "final=%d+%d", &t1, &t2) == 2) {
3284 final_x = t1;
3285 final_y = t2;
3286 }
3287 q = strstr(str, "extra_move=");
3288 if (q && sscanf(q, "extra_move=%d+%d", &t1, &t2) == 2) {
3289 extra_x = t1;
3290 extra_y = t2;
3291 }
3292 q = strstr(str, "dt=");
3293 if (q && sscanf(q, "dt=%d", &t1) == 1) {
3294 dt = t1 * 1000;
3295 }
3296
3297 if (grab_check) {
3298 int read_init = 0;
3299
3300 if (*pg_init >=0 && *kg_init >=0) {
3301 pg1 = *pg_init;
3302 kg1 = *kg_init;
3303 read_init = 1;
3304 } else {
3305 grab_state(&pg1, &kg1);
3306 read_init = 0;
3307 }
3308
3309 if (!strcmp(flip, "M2S")) {
3310 if (ifneeded && pg1 == 1 && kg1 == 1) {
3311 rfbLog("bcx_xattach: M2S grab state is already what we want, skipping moves: %d,%d\n", pg1, kg1);
3312 return strdup("DONE,GRAB_OK");
3313 }
3314 } else if (!strcmp(flip, "S2M")) {
3315 if (ifneeded && pg1 == 0 && kg1 == 0) {
3316 rfbLog("bcx_xattach: S2M grab state is already what we want, skipping moves: %d,%d\n", pg1, kg1);
3317 return strdup("DONE,GRAB_OK");
3318 }
3319 }
3320
3321 if (read_init) {
3322 ;
3323 } else if (!strcmp(flip, "M2S")) {
3324 if (pg1 != 0 || kg1 != 0) {
3325 rfbLog("bcx_xattach: M2S init grab state incorrect: %d,%d\n", pg1, kg1);
3326 usleep(2*dt);
3327 grab_state(&pg1, &kg1);
3328 rfbLog("bcx_xattach: slept and retried, grab is now: %d,%d\n", pg1, kg1);
3329 }
3330 } else if (!strcmp(flip, "S2M")) {
3331 if (pg1 != 1 || kg1 != 1) {
3332 rfbLog("bcx_xattach: S2M init grab state incorrect: %d,%d\n", pg1, kg1);
3333 usleep(2*dt);
3334 grab_state(&pg1, &kg1);
3335 rfbLog("bcx_xattach: slept and retried, grab is now: %d,%d\n", pg1, kg1);
3336 }
3337 }
3338 if (!read_init) {
3339 *pg_init = pg1;
3340 *kg_init = kg1;
3341 }
3342 }
3343
3344 /*
3345 * A guide for BARCO xattach:
3346 *
3347 * For -cursor_rule 'b(0):%:t(1),t(1):%:b(0)'
3348 * down+M2S up+S2M
3349 * For -cursor_rule 'r(0):%:l(1),l(1):%:r(0)'
3350 * right+M2S left+S2M
3351 *
3352 * For -cursor_rule 't(0):%:b(1),b(1):%:t(0)'
3353 * up+M2S down+S2M
3354 * For -cursor_rule 'l(0):%:r(1),r(1):%:l(0)'
3355 * left+M2S right+S2M
3356 * For -cursor_rule 'l(0):%:r(1),r(1):%:l(0),r(0):%:l(1),l(1):%:r(0)'
3357 * left+M2S right+S2M (we used to do both 'right')
3358 */
3359
3360 if (!strcmp(flip, "M2S")) {
3361 if (!strcmp(dir, "up")) {
3362 pmove(shift, 0); /* go to top edge */
3363 usleep(dt);
3364 pmove(shift+1, 0); /* move 1 for MotionNotify */
3365 } else if (!strcmp(dir, "down")) {
3366 pmove(shift, dpy_y-1); /* go to bottom edge */
3367 usleep(dt);
3368 pmove(shift+1, dpy_y-1); /* move 1 for MotionNotify */
3369 } else if (!strcmp(dir, "left")) {
3370 pmove(0, shift); /* go to left edge */
3371 usleep(dt);
3372 pmove(0, shift+1); /* move 1 for MotionNotify */
3373 } else if (!strcmp(dir, "right")) {
3374 pmove(dpy_x-1, shift); /* go to right edge */
3375 usleep(dt);
3376 pmove(dpy_x-1, shift+1); /* move 1 for Motion Notify */
3377 }
3378 } else if (!strcmp(flip, "S2M")) {
3379 int dts = dt/2;
3380 if (!strcmp(dir, "up")) {
3381 pmove(shift, 2); /* Approach top edge in 3 moves. 1st move */
3382 usleep(dts);
3383 pmove(shift, 1); /* 2nd move */
3384 usleep(dts);
3385 pmove(shift, 0); /* 3rd move */
3386 usleep(dts);
3387 pmove(shift+1, 0); /* move 1 for MotionNotify */
3388 usleep(dts);
3389 pmove(shift+1, dpy_y-2); /* go to height-2 for extra pixel (slave y now == 0?) */
3390 usleep(dts);
3391 pmove(shift, dpy_y-2); /* move 1 for MotionNotify */
3392 usleep(dts);
3393 pmove(shift, 1); /* go to 1 to be sure slave y == 0 */
3394 usleep(dts);
3395 pmove(shift+1, 1); /* move 1 for MotionNotify */
3396 } else if (!strcmp(dir, "down")) {
3397 pmove(shift, dpy_y-3); /* Approach bottom edge in 3 moves. 1st move */
3398 usleep(dts);
3399 pmove(shift, dpy_y-2); /* 2nd move */
3400 usleep(dts);
3401 pmove(shift, dpy_y-1); /* 3rd move */
3402 usleep(dts);
3403 pmove(shift+1, dpy_y-1); /* move 1 for MotionNotify */
3404 usleep(dts);
3405 pmove(shift+1, 1); /* go to 1 for extra pixel (slave y now == dpy_y-1?) */
3406 usleep(dts);
3407 pmove(shift, 1); /* move 1 for MotionNotify */
3408 usleep(dts);
3409 pmove(shift, dpy_y-2); /* go to dpy_y-2 to be sure slave y == dpy_y-1 */
3410 usleep(dts);
3411 pmove(shift+1, dpy_y-2); /* move 1 for MotionNotify */
3412 } else if (!strcmp(dir, "left")) {
3413 pmove(2, shift); /* Approach left edge in 3 moves. 1st move */
3414 usleep(dts);
3415 pmove(1, shift); /* 2nd move */
3416 usleep(dts);
3417 pmove(0, shift); /* 3rd move */
3418 usleep(dts);
3419 pmove(0, shift+1); /* move 1 for MotionNotify */
3420 usleep(dts);
3421 pmove(dpy_x-2, shift+1); /* go to width-2 for extra pixel (slave x now == 0?) */
3422 usleep(dts);
3423 pmove(dpy_x-2, shift); /* move 1 for MotionNotify */
3424 usleep(dts);
3425 pmove(1, shift); /* go to 1 to be sure slave x == 0 */
3426 usleep(dts);
3427 pmove(1, shift+1); /* move 1 for MotionNotify */
3428 } else if (!strcmp(dir, "right")) {
3429 pmove(dpy_x-3, shift); /* Approach right edge in 3 moves. 1st move */
3430 usleep(dts);
3431 pmove(dpy_x-2, shift); /* 2nd move */
3432 usleep(dts);
3433 pmove(dpy_x-1, shift); /* 3rd move */
3434 usleep(dts);
3435 pmove(dpy_x-1, shift+1); /* move 1 for MotionNotify */
3436 usleep(dts);
3437 pmove(1, shift+1); /* go to 1 to extra pixel (slave x now == dpy_x-1?) */
3438 usleep(dts);
3439 pmove(1, shift); /* move 1 for MotionNotify */
3440 usleep(dts);
3441 pmove(dpy_x-2, shift); /* go to dpy_x-2 to be sure slave x == dpy_x-1 */
3442 usleep(dts);
3443 pmove(dpy_x-2, shift+1); /* move 1 for MotionNotify */
3444 }
3445 }
3446
3447 usleep(dt);
3448 pmove(final_x, final_y);
3449 usleep(dt);
3450
3451 if (extra_x >= 0 && extra_y >= 0) {
3452 pmove(extra_x, extra_y);
3453 usleep(dt);
3454 }
3455
3456 strcpy(_bcx_res, "DONE");
3457
3458 if (grab_check) {
3459 char st[64];
3460
3461 usleep(3*dt);
3462 grab_state(&pg2, &kg2);
3463
3464 if (!strcmp(flip, "M2S")) {
3465 if (pg2 != 1 || kg2 != 1) {
3466 rfbLog("bcx_xattach: M2S fini grab state incorrect: %d,%d\n", pg2, kg2);
3467 usleep(2*dt);
3468 grab_state(&pg2, &kg2);
3469 rfbLog("bcx_xattach: slept and retried, grab is now: %d,%d\n", pg2, kg2);
3470 }
3471 } else if (!strcmp(flip, "S2M")) {
3472 if (pg2 != 0 || kg2 != 0) {
3473 rfbLog("bcx_xattach: S2M fini grab state incorrect: %d,%d\n", pg2, kg2);
3474 usleep(2*dt);
3475 grab_state(&pg2, &kg2);
3476 rfbLog("bcx_xattach: slept and retried, grab is now: %d,%d\n", pg2, kg2);
3477 }
3478 }
3479
3480 sprintf(st, ":%d,%d-%d,%d", pg1, kg1, pg2, kg2);
3481
3482 if (getenv("GRAB_CHECK_LOOP")) {
3483 int i, n = atoi(getenv("GRAB_CHECK_LOOP"));
3484 rfbLog("grab st: %s\n", st);
3485 for (i=0; i < n; i++) {
3486 usleep(dt);
3487 grab_state(&pg2, &kg2);
3488 sprintf(st, ":%d,%d-%d,%d", pg1, kg1, pg2, kg2);
3489 rfbLog("grab st: %s\n", st);
3490 }
3491 }
3492
3493 if (!strcmp(flip, "M2S")) {
3494 if (pg1 == 0 && kg1 == 0 && pg2 == 1 && kg2 == 1) {
3495 strcat(_bcx_res, ",GRAB_OK");
3496 } else {
3497 rfbLog("bcx_xattach: M2S grab state incorrect: %d,%d -> %d,%d\n", pg1, kg1, pg2, kg2);
3498 strcat(_bcx_res, ",GRAB_FAIL");
3499 if (pg2 == 1 && kg2 == 1) {
3500 strcat(_bcx_res, "_INIT");
3501 } else if (pg1 == 0 && kg1 == 0) {
3502 strcat(_bcx_res, "_FINAL");
3503 }
3504 strcat(_bcx_res, st);
3505 }
3506 } else if (!strcmp(flip, "S2M")) {
3507 if (pg1 == 1 && kg1 == 1 && pg2 == 0 && kg2 == 0) {
3508 strcat(_bcx_res, ",GRAB_OK");
3509 } else {
3510 rfbLog("bcx_xattach: S2M grab state incorrect: %d,%d -> %d,%d\n", pg1, kg1, pg2, kg2);
3511 strcat(_bcx_res, ",GRAB_FAIL");
3512 if (pg2 == 0 && kg2 == 0) {
3513 strcat(_bcx_res, "_INIT");
3514 } else if (pg1 == 1 && kg1 == 1) {
3515 strcat(_bcx_res, "_FINAL");
3516 }
3517 strcat(_bcx_res, st);
3518 }
3519 }
3520 }
3521 return strdup(_bcx_res);
3522 }
3523
set_xprop(char * prop,Window win,char * value)3524 int set_xprop(char *prop, Window win, char *value) {
3525 int rc = -1;
3526 #if !NO_X11
3527 Atom aprop;
3528
3529 RAWFB_RET(rc)
3530
3531 if (!prop || !value) {
3532 return rc;
3533 }
3534 if (win == None) {
3535 win = rootwin;
3536 }
3537 aprop = XInternAtom(dpy, prop, False);
3538 if (aprop == None) {
3539 return rc;
3540 }
3541 rc = XChangeProperty(dpy, win, aprop, XA_STRING, 8,
3542 PropModeReplace, (unsigned char *)value, strlen(value));
3543 return rc;
3544 #else
3545 RAWFB_RET(rc)
3546 if (!prop || !win || !value) {}
3547 return rc;
3548 #endif /* NO_X11 */
3549 }
3550
get_xprop(char * prop,Window win)3551 char *get_xprop(char *prop, Window win) {
3552 #if NO_X11
3553 RAWFB_RET(NULL)
3554 if (!prop || !win) {}
3555 return NULL;
3556 #else
3557 Atom type, aprop;
3558 int format, slen, dlen;
3559 unsigned long nitems = 0, bytes_after = 0;
3560 unsigned char* data = NULL;
3561 char get_str[VNC_CONNECT_MAX+1];
3562
3563 RAWFB_RET(NULL)
3564
3565 if (prop == NULL || !strcmp(prop, "")) {
3566 return NULL;
3567 }
3568 if (win == None) {
3569 win = rootwin;
3570 }
3571 aprop = XInternAtom(dpy, prop, True);
3572 if (aprop == None) {
3573 return NULL;
3574 }
3575
3576 get_str[0] = '\0';
3577 slen = 0;
3578
3579 /* read the property value into get_str: */
3580 do {
3581 if (XGetWindowProperty(dpy, win, aprop, nitems/4,
3582 VNC_CONNECT_MAX/16, False, AnyPropertyType, &type,
3583 &format, &nitems, &bytes_after, &data) == Success) {
3584
3585 dlen = nitems * (format/8);
3586 if (slen + dlen > VNC_CONNECT_MAX) {
3587 /* too big */
3588 rfbLog("get_xprop: warning: truncating large '%s'"
3589 " string > %d bytes.\n", prop, VNC_CONNECT_MAX);
3590 XFree_wr(data);
3591 break;
3592 }
3593 memcpy(get_str+slen, data, dlen);
3594 slen += dlen;
3595 get_str[slen] = '\0';
3596 XFree_wr(data);
3597 }
3598 } while (bytes_after > 0);
3599
3600 get_str[VNC_CONNECT_MAX] = '\0';
3601 rfbLog("get_prop: read: '%s' = '%s'\n", prop, get_str);
3602
3603 return strdup(get_str);
3604 #endif /* NO_X11 */
3605 }
3606
3607 static char _win_fmt[1000];
3608
win_fmt(Window win,XWindowAttributes a)3609 static char *win_fmt(Window win, XWindowAttributes a) {
3610 memset(_win_fmt, 0, sizeof(_win_fmt));
3611 sprintf(_win_fmt, "0x%lx:%dx%dx%d+%d+%d-map:%d-bw:%d-cl:%d-vis:%d-bs:%d/%d",
3612 win, a.width, a.height, a.depth, a.x, a.y, a.map_state, a.border_width, a.class,
3613 (int) ((a.visual)->visualid), a.backing_store, a.save_under);
3614 return _win_fmt;
3615 }
3616
wininfo(Window win,int show_children)3617 char *wininfo(Window win, int show_children) {
3618 #if NO_X11
3619 RAWFB_RET(NULL)
3620 if (!win || !show_children) {}
3621 return NULL;
3622 #else
3623 XWindowAttributes attr;
3624 int n, size = X11VNC_REMOTE_MAX;
3625 char get_str[X11VNC_REMOTE_MAX+1];
3626 unsigned int nchildren;
3627 Window rr, pr, *children;
3628
3629 RAWFB_RET(NULL)
3630
3631 if (win == None) {
3632 return strdup("None");
3633 }
3634
3635 X_LOCK;
3636 if (!valid_window(win, &attr, 1)) {
3637 X_UNLOCK;
3638 return strdup("Invalid");
3639 }
3640 get_str[0] = '\0';
3641
3642 if (show_children) {
3643 XQueryTree_wr(dpy, win, &rr, &pr, &children, &nchildren);
3644 } else {
3645 nchildren = 1;
3646 children = (Window *) calloc(2 * sizeof(Window), 1);
3647 children[0] = win;
3648 }
3649 for (n=0; n < (int) nchildren; n++) {
3650 char tmp[32];
3651 char *str = "Invalid";
3652 Window w = children[n];
3653 if (valid_window(w, &attr, 1)) {
3654 if (!show_children) {
3655 str = win_fmt(w, attr);
3656 } else {
3657 sprintf(tmp, "0x%lx", w);
3658 str = tmp;
3659 }
3660 }
3661 if ((int) (strlen(get_str) + 1 + strlen(str)) >= size) {
3662 break;
3663 }
3664 if (n > 0) {
3665 strcat(get_str, ",");
3666 }
3667 strcat(get_str, str);
3668 }
3669 get_str[size] = '\0';
3670 if (!show_children) {
3671 free(children);
3672 } else if (nchildren) {
3673 XFree_wr(children);
3674 }
3675 rfbLog("wininfo computed: %s\n", get_str);
3676 X_UNLOCK;
3677
3678 return strdup(get_str);
3679 #endif /* NO_X11 */
3680 }
3681
3682 /*
3683 * check if client_connect has been set, if so make the reverse connections.
3684 */
send_client_connect(void)3685 static void send_client_connect(void) {
3686 if (client_connect != NULL) {
3687 char *str = client_connect;
3688 if (strstr(str, "cmd=") == str || strstr(str, "qry=") == str) {
3689 process_remote_cmd(client_connect, 0);
3690 } else if (strstr(str, "ans=") == str
3691 || strstr(str, "aro=") == str) {
3692 ;
3693 } else if (strstr(str, "ack=") == str) {
3694 ;
3695 } else {
3696 reverse_connect(client_connect);
3697 }
3698 free(client_connect);
3699 client_connect = NULL;
3700 }
3701 }
3702
3703 /*
3704 * monitor the various input methods
3705 */
check_connect_inputs(void)3706 void check_connect_inputs(void) {
3707
3708 if (unixpw_in_progress) return;
3709
3710 /* flush any already set: */
3711 send_client_connect();
3712
3713 /* connect file: */
3714 if (client_connect_file != NULL) {
3715 check_connect_file(client_connect_file);
3716 }
3717 send_client_connect();
3718
3719 /* VNC_CONNECT property (vncconnect program) */
3720 if (vnc_connect && *vnc_connect_str != '\0') {
3721 client_connect = strdup(vnc_connect_str);
3722 vnc_connect_str[0] = '\0';
3723 }
3724 send_client_connect();
3725
3726 /* X11VNC_REMOTE property */
3727 if (vnc_connect && *x11vnc_remote_str != '\0') {
3728 client_connect = strdup(x11vnc_remote_str);
3729 x11vnc_remote_str[0] = '\0';
3730 }
3731 send_client_connect();
3732 }
3733
check_gui_inputs(void)3734 void check_gui_inputs(void) {
3735 int i, gnmax = 0, n = 0, nfds;
3736 int socks[ICON_MODE_SOCKS];
3737 fd_set fds;
3738 struct timeval tv;
3739 char buf[X11VNC_REMOTE_MAX+1];
3740 ssize_t nbytes;
3741
3742 if (unixpw_in_progress) return;
3743
3744 for (i=0; i<ICON_MODE_SOCKS; i++) {
3745 if (icon_mode_socks[i] >= 0) {
3746 socks[n++] = i;
3747 if (icon_mode_socks[i] > gnmax) {
3748 gnmax = icon_mode_socks[i];
3749 }
3750 }
3751 }
3752
3753 if (! n) {
3754 return;
3755 }
3756
3757 FD_ZERO(&fds);
3758 for (i=0; i<n; i++) {
3759 FD_SET(icon_mode_socks[socks[i]], &fds);
3760 }
3761 tv.tv_sec = 0;
3762 tv.tv_usec = 0;
3763
3764 nfds = select(gnmax+1, &fds, NULL, NULL, &tv);
3765 if (nfds <= 0) {
3766 return;
3767 }
3768
3769 for (i=0; i<n; i++) {
3770 int k, fd = icon_mode_socks[socks[i]];
3771 char *p;
3772 char **list;
3773 int lind;
3774
3775 if (! FD_ISSET(fd, &fds)) {
3776 continue;
3777 }
3778 for (k=0; k<=X11VNC_REMOTE_MAX; k++) {
3779 buf[k] = '\0';
3780 }
3781 nbytes = read(fd, buf, X11VNC_REMOTE_MAX);
3782 if (nbytes <= 0) {
3783 close(fd);
3784 icon_mode_socks[socks[i]] = -1;
3785 continue;
3786 }
3787
3788 list = (char **) calloc((strlen(buf)+2) * sizeof(char *), 1);
3789
3790 lind = 0;
3791 p = strtok(buf, "\r\n");
3792 while (p) {
3793 list[lind++] = strdup(p);
3794 p = strtok(NULL, "\r\n");
3795 }
3796
3797 lind = 0;
3798 while (list[lind] != NULL) {
3799 p = list[lind++];
3800 if (strstr(p, "cmd=") == p ||
3801 strstr(p, "qry=") == p) {
3802 char *str = process_remote_cmd(p, 1);
3803 if (! str) {
3804 str = strdup("");
3805 }
3806 nbytes = write(fd, str, strlen(str));
3807 write(fd, "\n", 1);
3808 free(str);
3809 if (nbytes < 0) {
3810 close(fd);
3811 icon_mode_socks[socks[i]] = -1;
3812 break;
3813 }
3814 }
3815 }
3816
3817 lind = 0;
3818 while (list[lind] != NULL) {
3819 p = list[lind++];
3820 if (p) free(p);
3821 }
3822 free(list);
3823 }
3824 }
3825
create_new_client(int sock,int start_thread)3826 rfbClientPtr create_new_client(int sock, int start_thread) {
3827 rfbClientPtr cl;
3828
3829 if (!screen) {
3830 return NULL;
3831 }
3832
3833 cl = rfbNewClient(screen, sock);
3834
3835 if (cl == NULL) {
3836 return NULL;
3837 }
3838 if (use_threads) {
3839 cl->onHold = FALSE;
3840 if (start_thread) {
3841 rfbStartOnHoldClient(cl);
3842 }
3843 }
3844 return cl;
3845 }
3846
3847 static int turn_off_truecolor = 0;
3848
turn_off_truecolor_ad(rfbClientPtr client)3849 static void turn_off_truecolor_ad(rfbClientPtr client) {
3850 if (client) {}
3851 if (turn_off_truecolor) {
3852 rfbLog("turning off truecolor advertising.\n");
3853 /* mutex */
3854 screen->serverFormat.trueColour = FALSE;
3855 screen->displayHook = NULL;
3856 screen->serverFormat.redShift = 0;
3857 screen->serverFormat.greenShift = 0;
3858 screen->serverFormat.blueShift = 0;
3859 screen->serverFormat.redMax = 0;
3860 screen->serverFormat.greenMax = 0;
3861 screen->serverFormat.blueMax = 0;
3862 turn_off_truecolor = 0;
3863 }
3864 }
3865
3866 /*
3867 * some overrides for the local console text chat.
3868 * could be useful in general for local helpers.
3869 */
3870
password_check_chat_helper(rfbClientPtr cl,const char * response,int len)3871 rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len) {
3872 if (response || len) {}
3873 if (cl != chat_window_client) {
3874 rfbLog("invalid client during chat_helper login\n");
3875 return FALSE;
3876 } else {
3877 if (!cl->host) {
3878 rfbLog("empty cl->host during chat_helper login\n");
3879 return FALSE;
3880 }
3881 if (strcmp(cl->host, "127.0.0.1")) {
3882 rfbLog("invalid cl->host during chat_helper login: %s\n", cl->host);
3883 return FALSE;
3884 }
3885 rfbLog("chat_helper login accepted\n");
3886 return TRUE;
3887 }
3888 }
3889
new_client_chat_helper(rfbClientPtr client)3890 enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client) {
3891 if (client) {}
3892 client->clientGoneHook = client_gone_chat_helper;
3893 rfbLog("new chat helper\n");
3894 return(RFB_CLIENT_ACCEPT);
3895 }
3896
client_gone_chat_helper(rfbClientPtr client)3897 void client_gone_chat_helper(rfbClientPtr client) {
3898 if (client) {}
3899 rfbLog("finished chat helper\n");
3900 chat_window_client = NULL;
3901 }
3902
client_set_net(rfbClientPtr client)3903 void client_set_net(rfbClientPtr client) {
3904 ClientData *cd;
3905 if (client == NULL) {
3906 return;
3907 }
3908 cd = (ClientData *) client->clientData;
3909 if (cd == NULL) {
3910 return;
3911 }
3912 if (cd->client_port < 0) {
3913 double dt = dnow();
3914 cd->client_port = get_remote_port(client->sock);
3915 cd->server_port = get_local_port(client->sock);
3916 cd->server_ip = get_local_host(client->sock);
3917 cd->hostname = ip2host(client->host);
3918 rfbLog("client_set_net: %s %.4f\n", client->host, dnow() - dt);
3919 }
3920 }
3921 /*
3922 * libvncserver callback for when a new client connects
3923 */
new_client(rfbClientPtr client)3924 enum rfbNewClientAction new_client(rfbClientPtr client) {
3925 ClientData *cd;
3926
3927 CLIENT_LOCK;
3928
3929 last_event = last_input = time(NULL);
3930
3931 latest_client = client;
3932
3933 if (inetd) {
3934 /*
3935 * Set this so we exit as soon as connection closes,
3936 * otherwise client_gone is only called after RFB_CLIENT_ACCEPT
3937 */
3938 if (inetd_client == NULL) {
3939 inetd_client = client;
3940 client->clientGoneHook = client_gone;
3941 }
3942 }
3943
3944 clients_served++;
3945
3946 if (use_openssl || use_stunnel) {
3947 if (! ssl_initialized) {
3948 rfbLog("denying additional client: %s ssl not setup"
3949 " yet.\n", client->host);
3950 CLIENT_UNLOCK;
3951 return(RFB_CLIENT_REFUSE);
3952 }
3953 }
3954 if (unixpw_in_progress) {
3955 rfbLog("denying additional client: %s during -unixpw login.\n",
3956 client->host);
3957 CLIENT_UNLOCK;
3958 return(RFB_CLIENT_REFUSE);
3959 }
3960 if (connect_once) {
3961 if (screen->dontDisconnect && screen->neverShared) {
3962 if (! shared && accepted_client) {
3963 rfbLog("denying additional client: %s:%d\n",
3964 client->host, get_remote_port(client->sock));
3965 CLIENT_UNLOCK;
3966 return(RFB_CLIENT_REFUSE);
3967 }
3968 }
3969 }
3970
3971 if (ipv6_client_ip_str != NULL) {
3972 rfbLog("renaming client->host from '%s' to '%s'\n",
3973 client->host ? client->host : "", ipv6_client_ip_str);
3974 if (client->host) {
3975 free(client->host);
3976 }
3977 client->host = strdup(ipv6_client_ip_str);
3978 }
3979
3980 if (! check_access(client->host)) {
3981 rfbLog("denying client: %s does not match %s\n", client->host,
3982 allow_list ? allow_list : "(null)" );
3983 CLIENT_UNLOCK;
3984 return(RFB_CLIENT_REFUSE);
3985 }
3986
3987 if(use_multipointer && xi2_device_creation_in_progress) {
3988 rfbLog("denying additional client: %s during MD creation.\n", client->host);
3989 CLIENT_UNLOCK;
3990 return(RFB_CLIENT_REFUSE);
3991 }
3992
3993 client->clientData = (void *) calloc(sizeof(ClientData), 1);
3994 cd = (ClientData *) client->clientData;
3995
3996 /* see client_set_net() we delay the DNS lookups during handshake */
3997 cd->client_port = -1;
3998 cd->username = strdup("");
3999 cd->unixname = strdup("");
4000 cd->cursor_x_saved = cd->cursor_y_saved = -1;
4001
4002 cd->input[0] = '-';
4003 cd->login_viewonly = -1;
4004 cd->login_time = time(NULL);
4005 cd->ssl_helper_pid = 0;
4006
4007 if (use_openssl && openssl_last_helper_pid) {
4008 cd->ssl_helper_pid = openssl_last_helper_pid;
4009 openssl_last_helper_pid = 0;
4010 }
4011
4012 if (! accept_client(client)) {
4013 rfbLog("denying client: %s local user rejected connection.\n",
4014 client->host);
4015 rfbLog("denying client: accept_cmd=\"%s\"\n",
4016 accept_cmd ? accept_cmd : "(null)" );
4017
4018 free_client_data(client);
4019
4020 CLIENT_UNLOCK;
4021 return(RFB_CLIENT_REFUSE);
4022 }
4023
4024 /* We will RFB_CLIENT_ACCEPT or RFB_CLIENT_ON_HOLD from here on. */
4025
4026 if (passwdfile) {
4027 if (strstr(passwdfile, "read:") == passwdfile ||
4028 strstr(passwdfile, "cmd:") == passwdfile) {
4029 if (read_passwds(passwdfile)) {
4030 install_passwds();
4031 } else {
4032 rfbLog("problem reading: %s\n", passwdfile);
4033 clean_up_exit(1);
4034 }
4035 } else if (strstr(passwdfile, "custom:") == passwdfile) {
4036 if (screen) {
4037 /* mutex */
4038 screen->passwordCheck = custom_passwd_check;
4039 }
4040 }
4041 }
4042
4043 cd->uid = clients_served;
4044
4045 /*
4046 create new XInput2 master device and add it it to client
4047 */
4048 if(use_multipointer)
4049 {
4050 char tmp[256];
4051 snprintf(tmp, sizeof tmp, "x11vnc %s", client->host);
4052
4053 xi2_device_creation_in_progress = 1;
4054
4055 if((cd->ptr_id = createMD(dpy, tmp)) < 0) {
4056 rfbLog("ERROR creating XInput2 MD for client %s, denying client.\n", client->host);
4057 free_client_data(client);
4058 xi2_device_creation_in_progress = 0;
4059 CLIENT_UNLOCK;
4060 return(RFB_CLIENT_REFUSE);
4061 }
4062
4063 cd->kbd_id = getPairedMD(dpy, cd->ptr_id);
4064
4065 rfbLog("Created XInput2 MD %i %i for client %s.\n", cd->ptr_id, cd->kbd_id, client->host);
4066
4067 xi2_device_creation_in_progress = 0;
4068
4069 snprintf(tmp, sizeof tmp, "%i", cd->uid);
4070 cd->cursor = setClientCursor(dpy, cd->ptr_id, 0.4*(cd->ptr_id%3), 0.2*(cd->ptr_id%5), 1*(cd->ptr_id%2), tmp);
4071 if(!cd->cursor)
4072 rfbLog("Setting cursor for client %s failed.\n", client->host);
4073
4074 cd->cursor_region = sraRgnCreate();
4075 }
4076
4077 client->clientGoneHook = client_gone;
4078
4079 if (client_count) {
4080 speeds_net_rate_measured = 0;
4081 speeds_net_latency_measured = 0;
4082 }
4083 client_count++;
4084
4085 last_keyboard_input = last_pointer_input = time(NULL);
4086
4087 if (no_autorepeat && client_count == 1 && ! view_only) {
4088 /*
4089 * first client, turn off X server autorepeat
4090 * XXX handle dynamic change of view_only and per-client.
4091 */
4092 autorepeat(0, 0);
4093 }
4094 #ifdef MACOSX
4095 if (macosx_console && client_count == 1) {
4096 macosxCG_refresh_callback_on();
4097 }
4098 #endif
4099 if (use_solid_bg && client_count == 1) {
4100 solid_bg(0);
4101 }
4102
4103 if (pad_geometry) {
4104 install_padded_fb(pad_geometry);
4105 }
4106
4107 cd->timer = last_new_client = dnow();
4108 cd->send_cmp_rate = 0.0;
4109 cd->send_raw_rate = 0.0;
4110 cd->latency = 0.0;
4111 cd->cmp_bytes_sent = 0;
4112 cd->raw_bytes_sent = 0;
4113
4114 accepted_client++;
4115 rfbLog("incr accepted_client=%d for %s:%d sock=%d\n", accepted_client,
4116 client->host, get_remote_port(client->sock), client->sock);
4117 last_client = time(NULL);
4118
4119 if (ncache) {
4120 check_ncache(1, 0);
4121 }
4122
4123 if (advertise_truecolor && indexed_color) {
4124 int rs = 0, gs = 2, bs = 4;
4125 int rm = 3, gm = 3, bm = 3;
4126 if (bpp >= 24) {
4127 rs = 0, gs = 8, bs = 16;
4128 rm = 255, gm = 255, bm = 255;
4129 } else if (bpp >= 16) {
4130 rs = 0, gs = 5, bs = 10;
4131 rm = 31, gm = 31, bm = 31;
4132 }
4133 rfbLog("advertising truecolor.\n");
4134 if (getenv("ADVERT_BMSHIFT")) {
4135 bm--;
4136 }
4137
4138 if (use_threads) LOCK(client->updateMutex);
4139
4140 client->format.trueColour = TRUE;
4141 client->format.redShift = rs;
4142 client->format.greenShift = gs;
4143 client->format.blueShift = bs;
4144 client->format.redMax = rm;
4145 client->format.greenMax = gm;
4146 client->format.blueMax = bm;
4147
4148 if (use_threads) UNLOCK(client->updateMutex);
4149
4150 rfbSetTranslateFunction(client);
4151
4152 /* mutex */
4153 screen->serverFormat.trueColour = TRUE;
4154 screen->serverFormat.redShift = rs;
4155 screen->serverFormat.greenShift = gs;
4156 screen->serverFormat.blueShift = bs;
4157 screen->serverFormat.redMax = rm;
4158 screen->serverFormat.greenMax = gm;
4159 screen->serverFormat.blueMax = bm;
4160 screen->displayHook = turn_off_truecolor_ad;
4161
4162 turn_off_truecolor = 1;
4163 }
4164
4165 if (unixpw) {
4166 unixpw_in_progress = 1;
4167 unixpw_client = client;
4168 unixpw_login_viewonly = 0;
4169
4170 unixpw_file_xfer_save = screen->permitFileTransfer;
4171 screen->permitFileTransfer = FALSE;
4172 unixpw_tightvnc_xfer_save = tightfilexfer;
4173 tightfilexfer = 0;
4174 #ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER
4175 rfbLog("rfbUnregisterTightVNCFileTransferExtension: 1\n");
4176 rfbUnregisterTightVNCFileTransferExtension();
4177 #endif
4178
4179 if (client->viewOnly) {
4180 unixpw_login_viewonly = 1;
4181 client->viewOnly = FALSE;
4182 }
4183 unixpw_last_try_time = time(NULL) + 10;
4184
4185 unixpw_screen(1);
4186 unixpw_keystroke(0, 0, 1);
4187
4188 if (!unixpw_in_rfbPE) {
4189 rfbLog("new client: %s in non-unixpw_in_rfbPE.\n",
4190 client->host);
4191 }
4192 CLIENT_UNLOCK;
4193 if (!use_threads) {
4194 /* always put client on hold even if unixpw_in_rfbPE is true */
4195 return(RFB_CLIENT_ON_HOLD);
4196 } else {
4197 /* unixpw threads is still in testing mode, disabled by default. See UNIXPW_THREADS */
4198 return(RFB_CLIENT_ACCEPT);
4199 }
4200 }
4201
4202 CLIENT_UNLOCK;
4203 return(RFB_CLIENT_ACCEPT);
4204 }
4205
start_client_info_sock(char * host_port_cookie)4206 void start_client_info_sock(char *host_port_cookie) {
4207 char *host = NULL, *cookie = NULL, *p;
4208 char *str = strdup(host_port_cookie);
4209 int i, port, sock, next = -1;
4210 static time_t start_time[ICON_MODE_SOCKS];
4211 time_t oldest = 0;
4212 int db = 0;
4213
4214 port = -1;
4215
4216 for (i = 0; i < ICON_MODE_SOCKS; i++) {
4217 if (icon_mode_socks[i] < 0) {
4218 next = i;
4219 break;
4220 }
4221 if (oldest == 0 || start_time[i] < oldest) {
4222 next = i;
4223 oldest = start_time[i];
4224 }
4225 }
4226
4227 p = strtok(str, ":");
4228 i = 0;
4229 while (p) {
4230 if (i == 0) {
4231 host = strdup(p);
4232 } else if (i == 1) {
4233 port = atoi(p);
4234 } else if (i == 2) {
4235 cookie = strdup(p);
4236 }
4237 i++;
4238 p = strtok(NULL, ":");
4239 }
4240 free(str);
4241
4242 if (db) fprintf(stderr, "%s/%d/%s next=%d\n", host, port, cookie, next);
4243
4244 if (host && port && cookie) {
4245 if (*host == '\0') {
4246 free(host);
4247 host = strdup("localhost");
4248 }
4249 sock = connect_tcp(host, port);
4250 if (sock < 0) {
4251 usleep(200 * 1000);
4252 sock = connect_tcp(host, port);
4253 }
4254 if (sock >= 0) {
4255 char *lst = list_clients();
4256 icon_mode_socks[next] = sock;
4257 start_time[next] = time(NULL);
4258 write(sock, "COOKIE:", strlen("COOKIE:"));
4259 write(sock, cookie, strlen(cookie));
4260 write(sock, "\n", strlen("\n"));
4261 write(sock, "none\n", strlen("none\n"));
4262 write(sock, "none\n", strlen("none\n"));
4263 write(sock, lst, strlen(lst));
4264 write(sock, "\n", strlen("\n"));
4265 if (db) {
4266 fprintf(stderr, "list: %s\n", lst);
4267 }
4268 free(lst);
4269 rfbLog("client_info_sock to: %s:%d\n", host, port);
4270 } else {
4271 rfbLog("failed client_info_sock: %s:%d\n", host, port);
4272 }
4273 } else {
4274 rfbLog("malformed client_info_sock: %s\n", host_port_cookie);
4275 }
4276
4277 if (host) free(host);
4278 if (cookie) free(cookie);
4279 }
4280
send_client_info(char * str)4281 void send_client_info(char *str) {
4282 int i;
4283 static char *pstr = NULL;
4284 static int len = 128;
4285
4286 if (!str || strlen(str) == 0) {
4287 return;
4288 }
4289
4290 if (!pstr) {
4291 pstr = (char *)malloc(len);
4292 }
4293 if (strlen(str) + 2 > (size_t) len) {
4294 free(pstr);
4295 len *= 2;
4296 pstr = (char *)malloc(len);
4297 }
4298 strcpy(pstr, str);
4299 strcat(pstr, "\n");
4300
4301 if (icon_mode_fh) {
4302 if (0) fprintf(icon_mode_fh, "\n");
4303 fprintf(icon_mode_fh, "%s", pstr);
4304 fflush(icon_mode_fh);
4305 }
4306
4307 for (i=0; i<ICON_MODE_SOCKS; i++) {
4308 int len, n, sock = icon_mode_socks[i];
4309 char *buf = pstr;
4310
4311 if (sock < 0) {
4312 continue;
4313 }
4314
4315 len = strlen(pstr);
4316 while (len > 0) {
4317 if (0) write(sock, "\n", 1);
4318 n = write(sock, buf, len);
4319 if (n > 0) {
4320 buf += n;
4321 len -= n;
4322 continue;
4323 }
4324
4325 if (n < 0 && errno == EINTR) {
4326 continue;
4327 }
4328 close(sock);
4329 icon_mode_socks[i] = -1;
4330 break;
4331 }
4332 }
4333 }
4334
adjust_grabs(int grab,int quiet)4335 void adjust_grabs(int grab, int quiet) {
4336 RAWFB_RET_VOID
4337 #if NO_X11
4338 if (!grab || !quiet) {}
4339 return;
4340 #else
4341 /* n.b. caller decides to X_LOCK or not. */
4342 if (grab) {
4343 if (grab_kbd) {
4344 if (! quiet) {
4345 rfbLog("grabbing keyboard with XGrabKeyboard\n");
4346 }
4347 XGrabKeyboard(dpy, window, False, GrabModeAsync,
4348 GrabModeAsync, CurrentTime);
4349 }
4350 if (grab_ptr) {
4351 if (! quiet) {
4352 rfbLog("grabbing pointer with XGrabPointer\n");
4353 }
4354 XGrabPointer(dpy, window, False, 0, GrabModeAsync,
4355 GrabModeAsync, None, None, CurrentTime);
4356 }
4357 } else {
4358 if (grab_kbd) {
4359 if (! quiet) {
4360 rfbLog("ungrabbing keyboard with XUngrabKeyboard\n");
4361 }
4362 XUngrabKeyboard(dpy, CurrentTime);
4363 }
4364 if (grab_ptr) {
4365 if (! quiet) {
4366 rfbLog("ungrabbing pointer with XUngrabPointer\n");
4367 }
4368 XUngrabPointer(dpy, CurrentTime);
4369 }
4370 }
4371 #endif /* NO_X11 */
4372 }
4373
check_new_clients(void)4374 void check_new_clients(void) {
4375 static int last_count = -1;
4376 rfbClientIteratorPtr iter;
4377 rfbClientPtr cl;
4378 int i, send_info = 0;
4379 int run_after_accept = 0;
4380
4381 if (unixpw_in_progress) {
4382 static double lping = 0.0;
4383 if (lping < dnow() + 5) {
4384 mark_rect_as_modified(0, 0, 1, 1, 1);
4385 lping = dnow();
4386 }
4387 if (unixpw_client && unixpw_client->viewOnly) {
4388 unixpw_login_viewonly = 1;
4389 unixpw_client->viewOnly = FALSE;
4390 }
4391 if (time(NULL) > unixpw_last_try_time + 45) {
4392 rfbLog("unixpw_deny: timed out waiting for reply.\n");
4393 unixpw_deny();
4394 }
4395 return;
4396 }
4397
4398 if (grab_always) {
4399 ;
4400 } else if (grab_kbd || grab_ptr) {
4401 static double last_force = 0.0;
4402 if (client_count != last_count || dnow() > last_force + 0.25) {
4403 int q = (client_count == last_count);
4404 last_force = dnow();
4405 X_LOCK;
4406 if (client_count) {
4407 adjust_grabs(1, q);
4408 } else {
4409 adjust_grabs(0, q);
4410 }
4411 X_UNLOCK;
4412 }
4413 }
4414
4415 if (last_count == -1) {
4416 last_count = 0;
4417 } else if (client_count == last_count) {
4418 return;
4419 }
4420
4421 if (! all_clients_initialized()) {
4422 return;
4423 }
4424
4425 if (client_count > last_count) {
4426 if (afteraccept_cmd != NULL && afteraccept_cmd[0] != '\0') {
4427 run_after_accept = 1;
4428 }
4429 }
4430
4431 last_count = client_count;
4432
4433 if (! screen) {
4434 return;
4435 }
4436
4437 if (! client_count) {
4438 send_client_info("clients:none");
4439 return;
4440 }
4441
4442 iter = rfbGetClientIterator(screen);
4443 while( (cl = rfbClientIteratorNext(iter)) ) {
4444 ClientData *cd = (ClientData *) cl->clientData;
4445 char *s;
4446
4447 client_set_net(cl);
4448 if (! cd) {
4449 continue;
4450 }
4451
4452 if (cd->login_viewonly < 0) {
4453 /* this is a general trigger to initialize things */
4454 if (cl->viewOnly) {
4455 cd->login_viewonly = 1;
4456 s = allowed_input_view_only;
4457 if (s && cd->input[0] == '-') {
4458 cl->viewOnly = FALSE;
4459 cd->input[0] = '\0';
4460 strncpy(cd->input, s, CILEN);
4461 }
4462 } else {
4463 cd->login_viewonly = 0;
4464 s = allowed_input_normal;
4465 if (s && cd->input[0] == '-') {
4466 cd->input[0] = '\0';
4467 strncpy(cd->input, s, CILEN);
4468 }
4469 }
4470 if (run_after_accept) {
4471 run_user_command(afteraccept_cmd, cl,
4472 "afteraccept", NULL, 0, NULL);
4473 }
4474 }
4475 }
4476 rfbReleaseClientIterator(iter);
4477
4478 if (icon_mode_fh) {
4479 send_info++;
4480 }
4481 for (i = 0; i < ICON_MODE_SOCKS; i++) {
4482 if (send_info || icon_mode_socks[i] >= 0) {
4483 send_info++;
4484 break;
4485 }
4486 }
4487 if (send_info) {
4488 char *str, *s = list_clients();
4489 str = (char *) malloc(strlen("clients:") + strlen(s) + 1);
4490 sprintf(str, "clients:%s", s);
4491 send_client_info(str);
4492 free(str);
4493 free(s);
4494 }
4495 }
4496
4497