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