You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3325 lines
73 KiB
3325 lines
73 KiB
/* -- connections.c -- */
|
|
|
|
#include "x11vnc.h"
|
|
#include "inet.h"
|
|
#include "remote.h"
|
|
#include "keyboard.h"
|
|
#include "cleanup.h"
|
|
#include "gui.h"
|
|
#include "solid.h"
|
|
#include "rates.h"
|
|
#include "screen.h"
|
|
#include "unixpw.h"
|
|
#include "user.h"
|
|
#include "scan.h"
|
|
#include "sslcmds.h"
|
|
#include "sslhelper.h"
|
|
#include "xwrappers.h"
|
|
#include "xevents.h"
|
|
#include "macosx.h"
|
|
#include "macosxCG.h"
|
|
#include "userinput.h"
|
|
|
|
/*
|
|
* routines for handling incoming, outgoing, etc connections
|
|
*/
|
|
|
|
/* string for the VNC_CONNECT property */
|
|
char vnc_connect_str[VNC_CONNECT_MAX+1];
|
|
Atom vnc_connect_prop = None;
|
|
char x11vnc_remote_str[X11VNC_REMOTE_MAX+1];
|
|
Atom x11vnc_remote_prop = None;
|
|
rfbClientPtr inetd_client = NULL;
|
|
|
|
int all_clients_initialized(void);
|
|
char *list_clients(void);
|
|
int new_fb_size_clients(rfbScreenInfoPtr s);
|
|
void close_all_clients(void);
|
|
void close_clients(char *str);
|
|
void set_client_input(char *str);
|
|
void set_child_info(void);
|
|
int cmd_ok(char *cmd);
|
|
void client_gone(rfbClientPtr client);
|
|
void client_gone_chat_helper(rfbClientPtr client);
|
|
void reverse_connect(char *str);
|
|
void set_vnc_connect_prop(char *str);
|
|
void read_vnc_connect_prop(int);
|
|
void set_x11vnc_remote_prop(char *str);
|
|
void read_x11vnc_remote_prop(int);
|
|
void check_connect_inputs(void);
|
|
void check_gui_inputs(void);
|
|
enum rfbNewClientAction new_client(rfbClientPtr client);
|
|
enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client);
|
|
rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len);
|
|
void start_client_info_sock(char *host_port_cookie);
|
|
void send_client_info(char *str);
|
|
void adjust_grabs(int grab, int quiet);
|
|
void check_new_clients(void);
|
|
int accept_client(rfbClientPtr client);
|
|
int run_user_command(char *cmd, rfbClientPtr client, char *mode, char *input,
|
|
int len, FILE *output);
|
|
int check_access(char *addr);
|
|
|
|
static rfbClientPtr *client_match(char *str);
|
|
static void free_client_data(rfbClientPtr client);
|
|
static void ugly_geom(char *p, int *x, int *y);
|
|
static int ugly_window(char *addr, char *userhost, int X, int Y,
|
|
int timeout, char *mode, int accept);
|
|
static int action_match(char *action, int rc);
|
|
static void check_connect_file(char *file);
|
|
static void send_client_connect(void);
|
|
|
|
|
|
/*
|
|
* check that all clients are in RFB_NORMAL state
|
|
*/
|
|
int all_clients_initialized(void) {
|
|
rfbClientIteratorPtr iter;
|
|
rfbClientPtr cl;
|
|
int ok = 1;
|
|
|
|
if (! screen) {
|
|
return ok;
|
|
}
|
|
|
|
iter = rfbGetClientIterator(screen);
|
|
while( (cl = rfbClientIteratorNext(iter)) ) {
|
|
if (cl->state != RFB_NORMAL) {
|
|
ok = 0;
|
|
break;
|
|
}
|
|
}
|
|
rfbReleaseClientIterator(iter);
|
|
|
|
return ok;
|
|
}
|
|
|
|
char *list_clients(void) {
|
|
rfbClientIteratorPtr iter;
|
|
rfbClientPtr cl;
|
|
char *list, tmp[256];
|
|
int count = 0;
|
|
|
|
if (!screen) {
|
|
return strdup("");
|
|
}
|
|
|
|
iter = rfbGetClientIterator(screen);
|
|
while( (cl = rfbClientIteratorNext(iter)) ) {
|
|
count++;
|
|
}
|
|
rfbReleaseClientIterator(iter);
|
|
|
|
/*
|
|
* each client:
|
|
* <id>:<ip>:<port>:<user>:<unix>:<hostname>:<input>:<loginview>:<time>,
|
|
* 8+1+64+1+5+1+24+1+24+1+256+1+5+1+1+1+10+1
|
|
* 123.123.123.123:60000/0x11111111-rw,
|
|
* so count+1 * 500 must cover it.
|
|
*/
|
|
list = (char *) malloc((count+1)*500);
|
|
|
|
list[0] = '\0';
|
|
|
|
iter = rfbGetClientIterator(screen);
|
|
while( (cl = rfbClientIteratorNext(iter)) ) {
|
|
ClientData *cd = (ClientData *) cl->clientData;
|
|
if (! cd) {
|
|
continue;
|
|
}
|
|
if (*list != '\0') {
|
|
strcat(list, ",");
|
|
}
|
|
sprintf(tmp, "0x%x:", cd->uid);
|
|
strcat(list, tmp);
|
|
strcat(list, cl->host);
|
|
strcat(list, ":");
|
|
sprintf(tmp, "%d:", cd->client_port);
|
|
strcat(list, tmp);
|
|
if (cd->username[0] == '\0') {
|
|
char *s = ident_username(cl);
|
|
if (s) free(s);
|
|
}
|
|
if (strstr(cd->username, "UNIX:") == cd->username) {
|
|
strcat(list, cd->username + strlen("UNIX:"));
|
|
} else {
|
|
strcat(list, cd->username);
|
|
}
|
|
strcat(list, ":");
|
|
if (cd->unixname[0] == '\0') {
|
|
strcat(list, "none");
|
|
} else {
|
|
strcat(list, cd->unixname);
|
|
}
|
|
strcat(list, ":");
|
|
strcat(list, cd->hostname);
|
|
strcat(list, ":");
|
|
strcat(list, cd->input);
|
|
strcat(list, ":");
|
|
sprintf(tmp, "%d", cd->login_viewonly);
|
|
strcat(list, tmp);
|
|
strcat(list, ":");
|
|
sprintf(tmp, "%d", (int) cd->login_time);
|
|
strcat(list, tmp);
|
|
}
|
|
rfbReleaseClientIterator(iter);
|
|
return list;
|
|
}
|
|
|
|
/* count number of clients supporting NewFBSize */
|
|
int new_fb_size_clients(rfbScreenInfoPtr s) {
|
|
rfbClientIteratorPtr iter;
|
|
rfbClientPtr cl;
|
|
int count = 0;
|
|
|
|
if (! s) {
|
|
return 0;
|
|
}
|
|
|
|
iter = rfbGetClientIterator(s);
|
|
while( (cl = rfbClientIteratorNext(iter)) ) {
|
|
if (cl->useNewFBSize) {
|
|
count++;
|
|
}
|
|
}
|
|
rfbReleaseClientIterator(iter);
|
|
return count;
|
|
}
|
|
|
|
void close_all_clients(void) {
|
|
rfbClientIteratorPtr iter;
|
|
rfbClientPtr cl;
|
|
|
|
if (! screen) {
|
|
return;
|
|
}
|
|
|
|
iter = rfbGetClientIterator(screen);
|
|
while( (cl = rfbClientIteratorNext(iter)) ) {
|
|
rfbCloseClient(cl);
|
|
rfbClientConnectionGone(cl);
|
|
}
|
|
rfbReleaseClientIterator(iter);
|
|
}
|
|
|
|
static rfbClientPtr *client_match(char *str) {
|
|
rfbClientIteratorPtr iter;
|
|
rfbClientPtr cl, *cl_list;
|
|
int i, n, host_warn = 0, hex_warn = 0;
|
|
|
|
n = client_count + 10;
|
|
cl_list = (rfbClientPtr *) malloc(n * sizeof(rfbClientPtr));
|
|
|
|
i = 0;
|
|
iter = rfbGetClientIterator(screen);
|
|
while( (cl = rfbClientIteratorNext(iter)) ) {
|
|
if (strstr(str, "0x") == str) {
|
|
unsigned int in;
|
|
int id;
|
|
ClientData *cd = (ClientData *) cl->clientData;
|
|
if (! cd) {
|
|
continue;
|
|
}
|
|
if (sscanf(str, "0x%x", &in) != 1) {
|
|
if (hex_warn++) {
|
|
continue;
|
|
}
|
|
rfbLog("skipping invalid client hex id: %s\n",
|
|
str);
|
|
continue;
|
|
}
|
|
id = (unsigned int) in;
|
|
if (cd->uid == id) {
|
|
cl_list[i++] = cl;
|
|
}
|
|
} else {
|
|
char *rstr = str;
|
|
if (! dotted_ip(str)) {
|
|
rstr = host2ip(str);
|
|
if (rstr == NULL || *rstr == '\0') {
|
|
if (host_warn++) {
|
|
continue;
|
|
}
|
|
rfbLog("skipping bad lookup: \"%s\"\n",
|
|
str);
|
|
continue;
|
|
}
|
|
rfbLog("lookup: %s -> %s\n", str, rstr);
|
|
}
|
|
if (!strcmp(rstr, cl->host)) {
|
|
cl_list[i++] = cl;
|
|
}
|
|
if (rstr != str) {
|
|
free(rstr);
|
|
}
|
|
}
|
|
if (i >= n - 1) {
|
|
break;
|
|
}
|
|
}
|
|
rfbReleaseClientIterator(iter);
|
|
|
|
cl_list[i] = NULL;
|
|
|
|
return cl_list;
|
|
}
|
|
|
|
void close_clients(char *str) {
|
|
rfbClientPtr *cl_list, *cp;
|
|
|
|
if (!strcmp(str, "all") || !strcmp(str, "*")) {
|
|
close_all_clients();
|
|
return;
|
|
}
|
|
|
|
if (! screen) {
|
|
return;
|
|
}
|
|
|
|
cl_list = client_match(str);
|
|
|
|
cp = cl_list;
|
|
while (*cp) {
|
|
rfbCloseClient(*cp);
|
|
rfbClientConnectionGone(*cp);
|
|
cp++;
|
|
}
|
|
free(cl_list);
|
|
}
|
|
|
|
void set_client_input(char *str) {
|
|
rfbClientPtr *cl_list, *cp;
|
|
char *p, *val;
|
|
|
|
/* str is "match:value" */
|
|
|
|
if (! screen) {
|
|
return;
|
|
}
|
|
|
|
p = strchr(str, ':');
|
|
if (! p) {
|
|
return;
|
|
}
|
|
*p = '\0';
|
|
p++;
|
|
val = short_kmbcf(p);
|
|
|
|
cl_list = client_match(str);
|
|
|
|
cp = cl_list;
|
|
while (*cp) {
|
|
ClientData *cd = (ClientData *) (*cp)->clientData;
|
|
if (! cd) {
|
|
continue;
|
|
}
|
|
cd->input[0] = '\0';
|
|
strcat(cd->input, "_");
|
|
strcat(cd->input, val);
|
|
cp++;
|
|
}
|
|
|
|
free(val);
|
|
free(cl_list);
|
|
}
|
|
|
|
void set_child_info(void) {
|
|
char pid[16];
|
|
/* set up useful environment for child process */
|
|
sprintf(pid, "%d", (int) getpid());
|
|
set_env("X11VNC_PID", pid);
|
|
if (program_name) {
|
|
/* e.g. for remote control -R */
|
|
set_env("X11VNC_PROG", program_name);
|
|
}
|
|
if (program_cmdline) {
|
|
set_env("X11VNC_CMDLINE", program_cmdline);
|
|
}
|
|
if (raw_fb_str) {
|
|
set_env("X11VNC_RAWFB_STR", raw_fb_str);
|
|
} else {
|
|
set_env("X11VNC_RAWFB_STR", "");
|
|
}
|
|
}
|
|
|
|
int cmd_ok(char *cmd) {
|
|
char *p, *str;
|
|
if (no_external_cmds) {
|
|
return 0;
|
|
}
|
|
if (! cmd || cmd[0] == '\0') {
|
|
return 0;
|
|
}
|
|
if (! allowed_external_cmds) {
|
|
/* default, allow any (overridden by -nocmds) */
|
|
return 1;
|
|
}
|
|
|
|
str = strdup(allowed_external_cmds);
|
|
p = strtok(str, ",");
|
|
while (p) {
|
|
if (!strcmp(p, cmd)) {
|
|
free(str);
|
|
return 1;
|
|
}
|
|
p = strtok(NULL, ",");
|
|
}
|
|
free(str);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* utility to run a user supplied command setting some RFB_ env vars.
|
|
* used by, e.g., accept_client() and client_gone()
|
|
*/
|
|
int run_user_command(char *cmd, rfbClientPtr client, char *mode, char *input,
|
|
int len, FILE *output) {
|
|
char *old_display = NULL;
|
|
char *addr = NULL;
|
|
char str[100];
|
|
int rc, ok;
|
|
ClientData *cd = NULL;
|
|
if (client != NULL) {
|
|
cd = (ClientData *) client->clientData;
|
|
addr = client->host;
|
|
}
|
|
|
|
if (addr == NULL || addr[0] == '\0') {
|
|
addr = "unknown-host";
|
|
}
|
|
|
|
/* set RFB_CLIENT_ID to semi unique id for command to use */
|
|
if (cd && cd->uid) {
|
|
sprintf(str, "0x%x", cd->uid);
|
|
} else {
|
|
/* not accepted yet: */
|
|
sprintf(str, "0x%x", clients_served);
|
|
}
|
|
set_env("RFB_CLIENT_ID", str);
|
|
|
|
/* set RFB_CLIENT_IP to IP addr for command to use */
|
|
set_env("RFB_CLIENT_IP", addr);
|
|
|
|
/* set RFB_X11VNC_PID to our pid for command to use */
|
|
sprintf(str, "%d", (int) getpid());
|
|
set_env("RFB_X11VNC_PID", str);
|
|
|
|
if (client == NULL) {
|
|
;
|
|
} else if (client->state == RFB_PROTOCOL_VERSION) {
|
|
set_env("RFB_STATE", "PROTOCOL_VERSION");
|
|
} else if (client->state == RFB_SECURITY_TYPE) {
|
|
set_env("RFB_STATE", "SECURITY_TYPE");
|
|
} else if (client->state == RFB_AUTHENTICATION) {
|
|
set_env("RFB_STATE", "AUTHENTICATION");
|
|
} else if (client->state == RFB_INITIALISATION) {
|
|
set_env("RFB_STATE", "INITIALISATION");
|
|
} else if (client->state == RFB_NORMAL) {
|
|
set_env("RFB_STATE", "NORMAL");
|
|
} else {
|
|
set_env("RFB_STATE", "UNKNOWN");
|
|
}
|
|
if (certret_str) {
|
|
set_env("RFB_SSL_CLIENT_CERT", certret_str);
|
|
} else {
|
|
set_env("RFB_SSL_CLIENT_CERT", "");
|
|
}
|
|
|
|
/* set RFB_CLIENT_PORT to peer port for command to use */
|
|
if (cd && cd->client_port > 0) {
|
|
sprintf(str, "%d", cd->client_port);
|
|
} else if (client) {
|
|
sprintf(str, "%d", get_remote_port(client->sock));
|
|
}
|
|
set_env("RFB_CLIENT_PORT", str);
|
|
|
|
set_env("RFB_MODE", mode);
|
|
|
|
/*
|
|
* now do RFB_SERVER_IP and RFB_SERVER_PORT (i.e. us!)
|
|
* This will establish a 5-tuple (including tcp) the external
|
|
* program can potentially use to work out the virtual circuit
|
|
* for this connection.
|
|
*/
|
|
if (cd && cd->server_ip) {
|
|
set_env("RFB_SERVER_IP", cd->server_ip);
|
|
} else if (client) {
|
|
char *sip = get_local_host(client->sock);
|
|
set_env("RFB_SERVER_IP", sip);
|
|
if (sip) free(sip);
|
|
}
|
|
|
|
if (cd && cd->server_port > 0) {
|
|
sprintf(str, "%d", cd->server_port);
|
|
} else if (client) {
|
|
sprintf(str, "%d", get_local_port(client->sock));
|
|
}
|
|
set_env("RFB_SERVER_PORT", str);
|
|
|
|
if (cd) {
|
|
sprintf(str, "%d", cd->login_viewonly);
|
|
} else {
|
|
sprintf(str, "%d", -1);
|
|
}
|
|
set_env("RFB_LOGIN_VIEWONLY", str);
|
|
|
|
if (cd) {
|
|
sprintf(str, "%d", (int) cd->login_time);
|
|
} else {
|
|
sprintf(str, ">%d", (int) time(NULL));
|
|
}
|
|
set_env("RFB_LOGIN_TIME", str);
|
|
|
|
sprintf(str, "%d", (int) time(NULL));
|
|
set_env("RFB_CURRENT_TIME", str);
|
|
|
|
if (!cd || !cd->username || cd->username[0] == '\0') {
|
|
set_env("RFB_USERNAME", "unknown-user");
|
|
} else {
|
|
set_env("RFB_USERNAME", cd->username);
|
|
}
|
|
/*
|
|
* Better set DISPLAY to the one we are polling, if they
|
|
* want something trickier, they can handle on their own
|
|
* via environment, etc.
|
|
*/
|
|
if (getenv("DISPLAY")) {
|
|
old_display = strdup(getenv("DISPLAY"));
|
|
}
|
|
|
|
if (raw_fb && ! dpy) { /* raw_fb hack */
|
|
set_env("DISPLAY", "rawfb");
|
|
} else {
|
|
set_env("DISPLAY", DisplayString(dpy));
|
|
}
|
|
|
|
/*
|
|
* work out the number of clients (have to use client_count
|
|
* since there is deadlock in rfbGetClientIterator)
|
|
*/
|
|
sprintf(str, "%d", client_count);
|
|
set_env("RFB_CLIENT_COUNT", str);
|
|
|
|
/* gone, accept, afteraccept */
|
|
ok = 0;
|
|
if (!strcmp(mode, "env")) {
|
|
return 1;
|
|
}
|
|
if (!strcmp(mode, "accept") && cmd_ok("accept")) {
|
|
ok = 1;
|
|
}
|
|
if (!strcmp(mode, "afteraccept") && cmd_ok("afteraccept")) {
|
|
ok = 1;
|
|
}
|
|
if (!strcmp(mode, "gone") && cmd_ok("gone")) {
|
|
ok = 1;
|
|
}
|
|
if (!strcmp(mode, "cmd_verify") && cmd_ok("unixpw")) {
|
|
ok = 1;
|
|
}
|
|
if (!strcmp(mode, "read_passwds") && cmd_ok("passwdfile")) {
|
|
ok = 1;
|
|
}
|
|
if (!strcmp(mode, "custom_passwd") && cmd_ok("custom_passwd")) {
|
|
ok = 1;
|
|
}
|
|
if (no_external_cmds || !ok) {
|
|
rfbLogEnable(1);
|
|
rfbLog("cannot run external commands in -nocmds mode:\n");
|
|
rfbLog(" \"%s\"\n", cmd);
|
|
rfbLog(" exiting.\n");
|
|
clean_up_exit(1);
|
|
}
|
|
rfbLog("running command:\n");
|
|
if (!quiet) {
|
|
fprintf(stderr, "\n %s\n\n", cmd);
|
|
}
|
|
close_exec_fds();
|
|
|
|
if (output != NULL) {
|
|
FILE *ph = popen(cmd, "r");
|
|
char line[1024];
|
|
if (ph == NULL) {
|
|
rfbLog("popen(%s) failed", cmd);
|
|
rfbLogPerror("popen");
|
|
clean_up_exit(1);
|
|
}
|
|
while (fgets(line, 1024, ph) != NULL) {
|
|
if (0) fprintf(stderr, "line: %s", line);
|
|
fprintf(output, "%s", line);
|
|
}
|
|
rc = pclose(ph);
|
|
goto got_rc;
|
|
} else if (input != NULL) {
|
|
FILE *ph = popen(cmd, "w");
|
|
if (ph == NULL) {
|
|
rfbLog("popen(%s) failed", cmd);
|
|
rfbLogPerror("popen");
|
|
clean_up_exit(1);
|
|
}
|
|
write(fileno(ph), input, len);
|
|
rc = pclose(ph);
|
|
goto got_rc;
|
|
}
|
|
|
|
#if LIBVNCSERVER_HAVE_FORK
|
|
{
|
|
pid_t pid, pidw;
|
|
struct sigaction sa, intr, quit;
|
|
sigset_t omask;
|
|
|
|
sa.sa_handler = SIG_IGN;
|
|
sa.sa_flags = 0;
|
|
sigemptyset(&sa.sa_mask);
|
|
sigaction(SIGINT, &sa, &intr);
|
|
sigaction(SIGQUIT, &sa, &quit);
|
|
|
|
sigaddset(&sa.sa_mask, SIGCHLD);
|
|
sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask);
|
|
|
|
if ((pid = fork()) > 0 || pid == -1) {
|
|
|
|
if (pid != -1) {
|
|
pidw = waitpid(pid, &rc, 0);
|
|
}
|
|
|
|
sigaction(SIGINT, &intr, (struct sigaction *) NULL);
|
|
sigaction(SIGQUIT, &quit, (struct sigaction *) NULL);
|
|
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL);
|
|
|
|
if (pid == -1) {
|
|
fprintf(stderr, "could not fork\n");
|
|
rfbLogPerror("fork");
|
|
rc = system(cmd);
|
|
}
|
|
} else {
|
|
/* this should close port 5900, etc.. */
|
|
int fd;
|
|
sigaction(SIGINT, &intr, (struct sigaction *) NULL);
|
|
sigaction(SIGQUIT, &quit, (struct sigaction *) NULL);
|
|
sigprocmask(SIG_SETMASK, &omask, (sigset_t *) NULL);
|
|
for (fd=3; fd<256; fd++) {
|
|
close(fd);
|
|
}
|
|
/* XXX test more */
|
|
if (!strcmp(mode, "gone")) {
|
|
#if LIBVNCSERVER_HAVE_SETSID
|
|
setsid();
|
|
#else
|
|
setpgrp();
|
|
#endif
|
|
}
|
|
execlp("/bin/sh", "/bin/sh", "-c", cmd, (char *) NULL);
|
|
exit(1);
|
|
}
|
|
}
|
|
#else
|
|
rc = system(cmd);
|
|
#endif
|
|
got_rc:
|
|
|
|
if (rc >= 256) {
|
|
rc = rc/256;
|
|
}
|
|
rfbLog("command returned: %d\n", rc);
|
|
|
|
if (old_display) {
|
|
set_env("DISPLAY", old_display);
|
|
free(old_display);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void free_client_data(rfbClientPtr client) {
|
|
if (! client) {
|
|
return;
|
|
}
|
|
if (client->clientData) {
|
|
ClientData *cd = (ClientData *) client->clientData;
|
|
if (cd) {
|
|
if (cd->server_ip) {
|
|
free(cd->server_ip);
|
|
cd->server_ip = NULL;
|
|
}
|
|
if (cd->hostname) {
|
|
free(cd->hostname);
|
|
cd->hostname = NULL;
|
|
}
|
|
if (cd->username) {
|
|
free(cd->username);
|
|
cd->username = NULL;
|
|
}
|
|
if (cd->unixname) {
|
|
free(cd->unixname);
|
|
cd->unixname = NULL;
|
|
}
|
|
}
|
|
free(client->clientData);
|
|
client->clientData = NULL;
|
|
}
|
|
}
|
|
|
|
static int accepted_client = 0;
|
|
|
|
/*
|
|
* callback for when a client disconnects
|
|
*/
|
|
void client_gone(rfbClientPtr client) {
|
|
ClientData *cd = NULL;
|
|
|
|
client_count--;
|
|
if (client_count < 0) client_count = 0;
|
|
|
|
speeds_net_rate_measured = 0;
|
|
speeds_net_latency_measured = 0;
|
|
|
|
rfbLog("client_count: %d\n", client_count);
|
|
last_client_gone = dnow();
|
|
|
|
if (unixpw_in_progress && unixpw_client) {
|
|
if (client == unixpw_client) {
|
|
unixpw_in_progress = 0;
|
|
screen->permitFileTransfer = unixpw_file_xfer_save;
|
|
if ((tightfilexfer = unixpw_tightvnc_xfer_save)) {
|
|
#ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER
|
|
rfbLog("rfbRegisterTightVNCFileTransferExtension: 3\n");
|
|
rfbRegisterTightVNCFileTransferExtension();
|
|
#endif
|
|
}
|
|
unixpw_client = NULL;
|
|
copy_screen();
|
|
}
|
|
}
|
|
|
|
|
|
if (no_autorepeat && client_count == 0) {
|
|
autorepeat(1, 0);
|
|
}
|
|
if (use_solid_bg && client_count == 0) {
|
|
solid_bg(1);
|
|
}
|
|
if ((ncache || ncache0) && client_count == 0) {
|
|
kde_no_animate(1);
|
|
}
|
|
if (client->clientData) {
|
|
cd = (ClientData *) client->clientData;
|
|
if (cd->ssl_helper_pid > 0) {
|
|
int status;
|
|
rfbLog("sending SIGTERM to ssl_helper_pid: %d\n",
|
|
cd->ssl_helper_pid);
|
|
kill(cd->ssl_helper_pid, SIGTERM);
|
|
usleep(200*1000);
|
|
#if LIBVNCSERVER_HAVE_SYS_WAIT_H && LIBVNCSERVER_HAVE_WAITPID
|
|
waitpid(cd->ssl_helper_pid, &status, WNOHANG);
|
|
#endif
|
|
ssl_helper_pid(cd->ssl_helper_pid, -1); /* delete */
|
|
}
|
|
}
|
|
if (gone_cmd && *gone_cmd != '\0') {
|
|
if (strstr(gone_cmd, "popup") == gone_cmd) {
|
|
int x = -64000, y = -64000, timeout = 120;
|
|
char *userhost = ident_username(client);
|
|
char *addr, *p, *mode;
|
|
|
|
/* extract timeout */
|
|
if ((p = strchr(gone_cmd, ':')) != NULL) {
|
|
int in;
|
|
if (sscanf(p+1, "%d", &in) == 1) {
|
|
timeout = in;
|
|
}
|
|
}
|
|
/* extract geometry */
|
|
if ((p = strpbrk(gone_cmd, "+-")) != NULL) {
|
|
ugly_geom(p, &x, &y);
|
|
}
|
|
|
|
/* find mode: mouse, key, or both */
|
|
if (strstr(gone_cmd, "popupmouse") == gone_cmd) {
|
|
mode = "mouse_only";
|
|
} else if (strstr(gone_cmd, "popupkey") == gone_cmd) {
|
|
mode = "key_only";
|
|
} else {
|
|
mode = "both";
|
|
}
|
|
|
|
addr = client->host;
|
|
|
|
ugly_window(addr, userhost, x, y, timeout, mode, 0);
|
|
|
|
free(userhost);
|
|
} else {
|
|
rfbLog("client_gone: using cmd: %s\n", client->host);
|
|
run_user_command(gone_cmd, client, "gone", NULL,0,NULL);
|
|
}
|
|
}
|
|
|
|
free_client_data(client);
|
|
|
|
if (inetd && client == inetd_client) {
|
|
rfbLog("inetd viewer exited.\n");
|
|
if (gui_pid > 0) {
|
|
rfbLog("killing gui_pid %d\n", gui_pid);
|
|
kill(gui_pid, SIGTERM);
|
|
}
|
|
clean_up_exit(0);
|
|
}
|
|
if (connect_once) {
|
|
/*
|
|
* This non-exit is done for a bad passwd to be consistent
|
|
* with our RFB_CLIENT_REFUSE behavior in new_client() (i.e.
|
|
* we disconnect after 1 successful connection).
|
|
*/
|
|
if ((client->state == RFB_PROTOCOL_VERSION ||
|
|
client->state == RFB_SECURITY_TYPE ||
|
|
client->state == RFB_AUTHENTICATION) && accepted_client) {
|
|
rfbLog("connect_once: invalid password or early "
|
|
"disconnect.\n");
|
|
rfbLog("connect_once: waiting for next connection.\n");
|
|
accepted_client = 0;
|
|
return;
|
|
}
|
|
if (shared && client_count > 0) {
|
|
rfbLog("connect_once: other shared clients still "
|
|
"connected, not exiting.\n");
|
|
return;
|
|
}
|
|
|
|
rfbLog("viewer exited.\n");
|
|
if ((client_connect || connect_or_exit) && gui_pid > 0) {
|
|
rfbLog("killing gui_pid %d\n", gui_pid);
|
|
kill(gui_pid, SIGTERM);
|
|
}
|
|
clean_up_exit(0);
|
|
}
|
|
#ifdef MACOSX
|
|
if (macosx_console && client_count == 0) {
|
|
macosxCG_refresh_callback_off();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Simple routine to limit access via string compare. A power user will
|
|
* want to compile libvncserver with libwrap support and use /etc/hosts.allow.
|
|
*/
|
|
int check_access(char *addr) {
|
|
int allowed = 0;
|
|
int ssl = 0;
|
|
char *p, *list;
|
|
|
|
if (use_openssl || use_stunnel) {
|
|
ssl = 1;
|
|
}
|
|
if (deny_all) {
|
|
rfbLog("check_access: new connections are currently "
|
|
"blocked.\n");
|
|
return 0;
|
|
}
|
|
if (addr == NULL || *addr == '\0') {
|
|
rfbLog("check_access: denying empty host IP address string.\n");
|
|
return 0;
|
|
}
|
|
|
|
if (allow_list == NULL) {
|
|
/* set to "" to possibly append allow_once */
|
|
allow_list = strdup("");
|
|
}
|
|
if (*allow_list == '\0' && allow_once == NULL) {
|
|
/* no constraints, accept it */
|
|
return 1;
|
|
}
|
|
|
|
if (strchr(allow_list, '/')) {
|
|
/* a file of IP addresess or prefixes */
|
|
int len, len2 = 0;
|
|
struct stat sbuf;
|
|
FILE *in;
|
|
char line[1024], *q;
|
|
|
|
if (stat(allow_list, &sbuf) != 0) {
|
|
rfbLogEnable(1);
|
|
rfbLog("check_access: failure stating file: %s\n",
|
|
allow_list);
|
|
rfbLogPerror("stat");
|
|
clean_up_exit(1);
|
|
}
|
|
len = sbuf.st_size + 1; /* 1 more for '\0' at end */
|
|
if (allow_once) {
|
|
len2 = strlen(allow_once) + 2;
|
|
len += len2;
|
|
}
|
|
if (ssl) {
|
|
len2 = strlen("127.0.0.1") + 2;
|
|
len += len2;
|
|
}
|
|
list = (char *) malloc(len);
|
|
list[0] = '\0';
|
|
|
|
in = fopen(allow_list, "r");
|
|
if (in == NULL) {
|
|
rfbLogEnable(1);
|
|
rfbLog("check_access: cannot open: %s\n", allow_list);
|
|
rfbLogPerror("fopen");
|
|
clean_up_exit(1);
|
|
}
|
|
while (fgets(line, 1024, in) != NULL) {
|
|
if ( (q = strchr(line, '#')) != NULL) {
|
|
*q = '\0';
|
|
}
|
|
if (strlen(list) + strlen(line) >=
|
|
(size_t) (len - len2)) {
|
|
/* file grew since our stat() */
|
|
break;
|
|
}
|
|
strcat(list, line);
|
|
}
|
|
fclose(in);
|
|
if (allow_once) {
|
|
strcat(list, "\n");
|
|
strcat(list, allow_once);
|
|
strcat(list, "\n");
|
|
}
|
|
if (ssl) {
|
|
strcat(list, "\n");
|
|
strcat(list, "127.0.0.1");
|
|
strcat(list, "\n");
|
|
}
|
|
} else {
|
|
int len = strlen(allow_list) + 1;
|
|
if (allow_once) {
|
|
len += strlen(allow_once) + 1;
|
|
}
|
|
if (ssl) {
|
|
len += strlen("127.0.0.1") + 1;
|
|
}
|
|
list = (char *) malloc(len);
|
|
list[0] = '\0';
|
|
strcat(list, allow_list);
|
|
if (allow_once) {
|
|
strcat(list, ",");
|
|
strcat(list, allow_once);
|
|
}
|
|
if (ssl) {
|
|
strcat(list, ",");
|
|
strcat(list, "127.0.0.1");
|
|
}
|
|
}
|
|
|
|
if (allow_once) {
|
|
free(allow_once);
|
|
allow_once = NULL;
|
|
}
|
|
|
|
p = strtok(list, ", \t\n\r");
|
|
while (p) {
|
|
char *chk, *q, *r = NULL;
|
|
if (*p == '\0') {
|
|
p = strtok(NULL, ", \t\n\r");
|
|
continue;
|
|
}
|
|
if (! dotted_ip(p)) {
|
|
r = host2ip(p);
|
|
if (r == NULL || *r == '\0') {
|
|
rfbLog("check_access: bad lookup \"%s\"\n", p);
|
|
p = strtok(NULL, ", \t\n\r");
|
|
continue;
|
|
}
|
|
rfbLog("check_access: lookup %s -> %s\n", p, r);
|
|
chk = r;
|
|
} else {
|
|
chk = p;
|
|
}
|
|
|
|
q = strstr(addr, chk);
|
|
if (chk[strlen(chk)-1] != '.') {
|
|
if (!strcmp(addr, chk)) {
|
|
if (chk != p) {
|
|
rfbLog("check_access: client %s "
|
|
"matches host %s=%s\n", addr,
|
|
chk, p);
|
|
} else {
|
|
rfbLog("check_access: client %s "
|
|
"matches host %s\n", addr, chk);
|
|
}
|
|
allowed = 1;
|
|
} else if(!strcmp(chk, "localhost") &&
|
|
!strcmp(addr, "127.0.0.1")) {
|
|
allowed = 1;
|
|
}
|
|
} else if (q == addr) {
|
|
rfbLog("check_access: client %s matches pattern %s\n",
|
|
addr, chk);
|
|
allowed = 1;
|
|
}
|
|
p = strtok(NULL, ", \t\n\r");
|
|
if (r) {
|
|
free(r);
|
|
}
|
|
if (allowed) {
|
|
break;
|
|
}
|
|
}
|
|
free(list);
|
|
return allowed;
|
|
}
|
|
|
|
/*
|
|
* x11vnc's first (and only) visible widget: accept/reject dialog window.
|
|
* We go through this pain to avoid dependency on libXt...
|
|
*/
|
|
static int ugly_window(char *addr, char *userhost, int X, int Y,
|
|
int timeout, char *mode, int accept) {
|
|
#if NO_X11
|
|
if (!addr || !userhost || !X || !Y || !timeout || !mode || !accept) {}
|
|
RAWFB_RET(0)
|
|
nox11_exit(1);
|
|
return 0;
|
|
#else
|
|
|
|
#define t2x2_width 16
|
|
#define t2x2_height 16
|
|
static unsigned char t2x2_bits[] = {
|
|
0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff,
|
|
0x33, 0x33, 0x33, 0x33, 0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33,
|
|
0xff, 0xff, 0xff, 0xff, 0x33, 0x33, 0x33, 0x33};
|
|
|
|
Window awin;
|
|
GC gc;
|
|
XSizeHints hints;
|
|
XGCValues values;
|
|
static XFontStruct *font_info = NULL;
|
|
static Pixmap ico = 0;
|
|
unsigned long valuemask = 0;
|
|
static char dash_list[] = {20, 40};
|
|
int list_length = sizeof(dash_list);
|
|
|
|
Atom wm_protocols;
|
|
Atom wm_delete_window;
|
|
|
|
XEvent ev;
|
|
long evmask = ExposureMask | KeyPressMask | ButtonPressMask
|
|
| StructureNotifyMask;
|
|
double waited = 0.0;
|
|
|
|
/* strings and geometries y/n */
|
|
KeyCode key_y, key_n, key_v;
|
|
char strh[100];
|
|
char stri[100];
|
|
char str1_b[] = "To accept: press \"y\" or click the \"Yes\" button";
|
|
char str2_b[] = "To reject: press \"n\" or click the \"No\" button";
|
|
char str3_b[] = "View only: press \"v\" or click the \"View\" button";
|
|
char str1_m[] = "To accept: click the \"Yes\" button";
|
|
char str2_m[] = "To reject: click the \"No\" button";
|
|
char str3_m[] = "View only: click the \"View\" button";
|
|
char str1_k[] = "To accept: press \"y\"";
|
|
char str2_k[] = "To reject: press \"n\"";
|
|
char str3_k[] = "View only: press \"v\"";
|
|
char *str1, *str2, *str3;
|
|
char str_y[] = "Yes";
|
|
char str_n[] = "No";
|
|
char str_v[] = "View";
|
|
int x, y, w = 345, h = 175, ret = 0;
|
|
int X_sh = 20, Y_sh = 30, dY = 20;
|
|
int Ye_x = 20, Ye_y = 0, Ye_w = 45, Ye_h = 20;
|
|
int No_x = 75, No_y = 0, No_w = 45, No_h = 20;
|
|
int Vi_x = 130, Vi_y = 0, Vi_w = 45, Vi_h = 20;
|
|
char *sprop = "new x11vnc client";
|
|
|
|
KeyCode key_o;
|
|
|
|
RAWFB_RET(0)
|
|
|
|
if (! accept) {
|
|
sprintf(str_y, "OK");
|
|
sprop = "x11vnc client disconnected";
|
|
h = 110;
|
|
str1 = "";
|
|
str2 = "";
|
|
str3 = "";
|
|
} else if (!strcmp(mode, "mouse_only")) {
|
|
str1 = str1_m;
|
|
str2 = str2_m;
|
|
str3 = str3_m;
|
|
} else if (!strcmp(mode, "key_only")) {
|
|
str1 = str1_k;
|
|
str2 = str2_k;
|
|
str3 = str3_k;
|
|
h -= dY;
|
|
} else {
|
|
str1 = str1_b;
|
|
str2 = str2_b;
|
|
str3 = str3_b;
|
|
}
|
|
if (view_only) {
|
|
h -= dY;
|
|
}
|
|
|
|
/* XXX handle coff_x/coff_y? */
|
|
if (X < -dpy_x) {
|
|
x = (dpy_x - w)/2; /* large negative: center */
|
|
if (x < 0) x = 0;
|
|
} else if (X < 0) {
|
|
x = dpy_x + X - w; /* from lower right */
|
|
} else {
|
|
x = X; /* from upper left */
|
|
}
|
|
|
|
if (Y < -dpy_y) {
|
|
y = (dpy_y - h)/2;
|
|
if (y < 0) y = 0;
|
|
} else if (Y < 0) {
|
|
y = dpy_y + Y - h;
|
|
} else {
|
|
y = Y;
|
|
}
|
|
|
|
X_LOCK;
|
|
|
|
awin = XCreateSimpleWindow(dpy, window, x, y, w, h, 4,
|
|
BlackPixel(dpy, scr), WhitePixel(dpy, scr));
|
|
|
|
wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
|
|
wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
|
|
XSetWMProtocols(dpy, awin, &wm_delete_window, 1);
|
|
|
|
if (! ico) {
|
|
ico = XCreateBitmapFromData(dpy, awin, (char *) t2x2_bits,
|
|
t2x2_width, t2x2_height);
|
|
}
|
|
|
|
hints.flags = PPosition | PSize | PMinSize;
|
|
hints.x = x;
|
|
hints.y = y;
|
|
hints.width = w;
|
|
hints.height = h;
|
|
hints.min_width = w;
|
|
hints.min_height = h;
|
|
|
|
XSetStandardProperties(dpy, awin, sprop, "x11vnc query", ico, NULL,
|
|
0, &hints);
|
|
|
|
XSelectInput_wr(dpy, awin, evmask);
|
|
|
|
if (! font_info && (font_info = XLoadQueryFont(dpy, "fixed")) == NULL) {
|
|
rfbLogEnable(1);
|
|
rfbLog("ugly_window: cannot locate font fixed.\n");
|
|
X_UNLOCK;
|
|
clean_up_exit(1);
|
|
}
|
|
|
|
gc = XCreateGC(dpy, awin, valuemask, &values);
|
|
XSetFont(dpy, gc, font_info->fid);
|
|
XSetForeground(dpy, gc, BlackPixel(dpy, scr));
|
|
XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter);
|
|
XSetDashes(dpy, gc, 0, dash_list, list_length);
|
|
|
|
XMapWindow(dpy, awin);
|
|
XFlush_wr(dpy);
|
|
|
|
if (accept) {
|
|
char *ip = addr;
|
|
char *type = "accept";
|
|
if (unixpw && strstr(userhost, "UNIX:") != userhost) {
|
|
type = "UNIXPW";
|
|
if (openssl_last_ip) {
|
|
ip = openssl_last_ip;
|
|
}
|
|
}
|
|
snprintf(strh, 100, "x11vnc: %s connection from %s?", type, ip);
|
|
} else {
|
|
snprintf(strh, 100, "x11vnc: client disconnected from %s", addr);
|
|
}
|
|
snprintf(stri, 100, " (%s)", userhost);
|
|
|
|
key_o = XKeysymToKeycode(dpy, XStringToKeysym("o"));
|
|
key_y = XKeysymToKeycode(dpy, XStringToKeysym("y"));
|
|
key_n = XKeysymToKeycode(dpy, XStringToKeysym("n"));
|
|
key_v = XKeysymToKeycode(dpy, XStringToKeysym("v"));
|
|
|
|
while (1) {
|
|
int out = -1, x, y, tw, k;
|
|
|
|
if (XCheckWindowEvent(dpy, awin, evmask, &ev)) {
|
|
; /* proceed to handling */
|
|
} else if (XCheckTypedEvent(dpy, ClientMessage, &ev)) {
|
|
; /* proceed to handling */
|
|
} else {
|
|
int ms = 100; /* sleep a bit */
|
|
usleep(ms * 1000);
|
|
waited += ((double) ms)/1000.;
|
|
if (timeout && (int) waited >= timeout) {
|
|
rfbLog("ugly_window: popup timed out after "
|
|
"%d seconds.\n", timeout);
|
|
out = 0;
|
|
ev.type = 0;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch(ev.type) {
|
|
case Expose:
|
|
while (XCheckTypedEvent(dpy, Expose, &ev)) {
|
|
;
|
|
}
|
|
k=0;
|
|
|
|
/* instructions */
|
|
XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
|
|
strh, strlen(strh));
|
|
XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
|
|
stri, strlen(stri));
|
|
if (accept) {
|
|
XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
|
|
str1, strlen(str1));
|
|
XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
|
|
str2, strlen(str2));
|
|
if (! view_only) {
|
|
XDrawString(dpy, awin, gc, X_sh, Y_sh+(k++)*dY,
|
|
str3, strlen(str3));
|
|
}
|
|
}
|
|
|
|
if (!strcmp(mode, "key_only")) {
|
|
break;
|
|
}
|
|
|
|
/* buttons */
|
|
Ye_y = Y_sh+k*dY;
|
|
No_y = Y_sh+k*dY;
|
|
Vi_y = Y_sh+k*dY;
|
|
XDrawRectangle(dpy, awin, gc, Ye_x, Ye_y, Ye_w, Ye_h);
|
|
|
|
if (accept) {
|
|
XDrawRectangle(dpy, awin, gc, No_x, No_y, No_w, No_h);
|
|
if (! view_only) {
|
|
XDrawRectangle(dpy, awin, gc, Vi_x, Vi_y,
|
|
Vi_w, Vi_h);
|
|
}
|
|
}
|
|
|
|
tw = XTextWidth(font_info, str_y, strlen(str_y));
|
|
tw = (Ye_w - tw)/2;
|
|
if (tw < 0) tw = 1;
|
|
XDrawString(dpy, awin, gc, Ye_x+tw, Ye_y+Ye_h-5,
|
|
str_y, strlen(str_y));
|
|
|
|
if (!accept) {
|
|
break;
|
|
}
|
|
tw = XTextWidth(font_info, str_n, strlen(str_n));
|
|
tw = (No_w - tw)/2;
|
|
if (tw < 0) tw = 1;
|
|
XDrawString(dpy, awin, gc, No_x+tw, No_y+No_h-5,
|
|
str_n, strlen(str_n));
|
|
|
|
if (! view_only) {
|
|
tw = XTextWidth(font_info, str_v,
|
|
strlen(str_v));
|
|
tw = (Vi_w - tw)/2;
|
|
if (tw < 0) tw = 1;
|
|
XDrawString(dpy, awin, gc, Vi_x+tw,
|
|
Vi_y+Vi_h-5, str_v, strlen(str_v));
|
|
}
|
|
|
|
break;
|
|
|
|
case ClientMessage:
|
|
if (ev.xclient.message_type == wm_protocols &&
|
|
(Atom) ev.xclient.data.l[0] == wm_delete_window) {
|
|
out = 0;
|
|
}
|
|
break;
|
|
|
|
case ButtonPress:
|
|
x = ev.xbutton.x;
|
|
y = ev.xbutton.y;
|
|
if (!strcmp(mode, "key_only")) {
|
|
;
|
|
} else if (x > Ye_x && x < Ye_x+Ye_w && y > Ye_y
|
|
&& y < Ye_y+Ye_h) {
|
|
out = 1;
|
|
} else if (! accept) {
|
|
;
|
|
} else if (x > No_x && x < No_x+No_w && y > No_y
|
|
&& y < No_y+No_h) {
|
|
out = 0;
|
|
} else if (! view_only && x > Vi_x && x < Vi_x+Vi_w
|
|
&& y > Vi_y && y < Vi_y+Ye_h) {
|
|
out = 2;
|
|
}
|
|
break;
|
|
|
|
case KeyPress:
|
|
if (!strcmp(mode, "mouse_only")) {
|
|
;
|
|
} else if (! accept) {
|
|
if (ev.xkey.keycode == key_o) {
|
|
out = 1;
|
|
}
|
|
if (ev.xkey.keycode == key_y) {
|
|
out = 1;
|
|
}
|
|
} else if (ev.xkey.keycode == key_y) {
|
|
out = 1;
|
|
;
|
|
} else if (ev.xkey.keycode == key_n) {
|
|
out = 0;
|
|
} else if (! view_only && ev.xkey.keycode == key_v) {
|
|
out = 2;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (out != -1) {
|
|
ret = out;
|
|
XSelectInput_wr(dpy, awin, 0);
|
|
XUnmapWindow(dpy, awin);
|
|
XFree_wr(gc);
|
|
XDestroyWindow(dpy, awin);
|
|
XFlush_wr(dpy);
|
|
break;
|
|
}
|
|
}
|
|
X_UNLOCK;
|
|
|
|
return ret;
|
|
#endif /* NO_X11 */
|
|
}
|
|
|
|
/*
|
|
* process a "yes:0,no:*,view:3" type action list comparing to command
|
|
* return code rc. * means the default action with no other match.
|
|
*/
|
|
static int action_match(char *action, int rc) {
|
|
char *p, *q, *s = strdup(action);
|
|
int cases[4], i, result;
|
|
char *labels[4];
|
|
|
|
labels[1] = "yes";
|
|
labels[2] = "no";
|
|
labels[3] = "view";
|
|
|
|
rfbLog("accept_client: process action line: %s\n",
|
|
action);
|
|
|
|
for (i=1; i <= 3; i++) {
|
|
cases[i] = -2;
|
|
}
|
|
|
|
p = strtok(s, ",");
|
|
while (p) {
|
|
if ((q = strchr(p, ':')) != NULL) {
|
|
int in, k = 1;
|
|
*q = '\0';
|
|
q++;
|
|
if (strstr(p, "yes") == p) {
|
|
k = 1;
|
|
} else if (strstr(p, "no") == p) {
|
|
k = 2;
|
|
} else if (strstr(p, "view") == p) {
|
|
k = 3;
|
|
} else {
|
|
rfbLogEnable(1);
|
|
rfbLog("invalid action line: %s\n", action);
|
|
clean_up_exit(1);
|
|
}
|
|
if (*q == '*') {
|
|
cases[k] = -1;
|
|
} else if (sscanf(q, "%d", &in) == 1) {
|
|
if (in < 0) {
|
|
rfbLogEnable(1);
|
|
rfbLog("invalid action line: %s\n",
|
|
action);
|
|
clean_up_exit(1);
|
|
}
|
|
cases[k] = in;
|
|
} else {
|
|
rfbLogEnable(1);
|
|
rfbLog("invalid action line: %s\n", action);
|
|
clean_up_exit(1);
|
|
}
|
|
} else {
|
|
rfbLogEnable(1);
|
|
rfbLog("invalid action line: %s\n", action);
|
|
clean_up_exit(1);
|
|
}
|
|
p = strtok(NULL, ",");
|
|
}
|
|
free(s);
|
|
|
|
result = -1;
|
|
for (i=1; i <= 3; i++) {
|
|
if (cases[i] == -1) {
|
|
rfbLog("accept_client: default action is case=%d %s\n",
|
|
i, labels[i]);
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
if (result == -1) {
|
|
rfbLog("accept_client: no default action\n");
|
|
}
|
|
for (i=1; i <= 3; i++) {
|
|
if (cases[i] >= 0 && cases[i] == rc) {
|
|
rfbLog("accept_client: matched action is case=%d %s\n",
|
|
i, labels[i]);
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
if (result < 0) {
|
|
rfbLog("no action match: %s rc=%d set to no\n", action, rc);
|
|
result = 2;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static void ugly_geom(char *p, int *x, int *y) {
|
|
int x1, y1;
|
|
|
|
if (sscanf(p, "+%d+%d", &x1, &y1) == 2) {
|
|
*x = x1;
|
|
*y = y1;
|
|
} else if (sscanf(p, "+%d-%d", &x1, &y1) == 2) {
|
|
*x = x1;
|
|
*y = -y1;
|
|
} else if (sscanf(p, "-%d+%d", &x1, &y1) == 2) {
|
|
*x = -x1;
|
|
*y = y1;
|
|
} else if (sscanf(p, "-%d-%d", &x1, &y1) == 2) {
|
|
*x = -x1;
|
|
*y = -y1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Simple routine to prompt the user on the X display whether an incoming
|
|
* client should be allowed to connect or not. If a gui is involved it
|
|
* will be running in the environment/context of the X11 DISPLAY.
|
|
*
|
|
* The command supplied via -accept is run as is (i.e. no string
|
|
* substitution) with the RFB_CLIENT_IP environment variable set to the
|
|
* incoming client's numerical IP address.
|
|
*
|
|
* If the external command exits with 0 the client is accepted, otherwise
|
|
* the client is rejected.
|
|
*
|
|
* Some builtins are provided:
|
|
*
|
|
* xmessage: use homebrew xmessage(1) for the external command.
|
|
* popup: use internal X widgets for prompting.
|
|
*
|
|
*/
|
|
int accept_client(rfbClientPtr client) {
|
|
|
|
char xmessage[200], *cmd = NULL;
|
|
char *addr = client->host;
|
|
char *action = NULL;
|
|
|
|
if (accept_cmd == NULL || *accept_cmd == '\0') {
|
|
return 1; /* no command specified, so we accept */
|
|
}
|
|
|
|
if (addr == NULL || addr[0] == '\0') {
|
|
addr = "unknown-host";
|
|
}
|
|
|
|
if (strstr(accept_cmd, "popup") == accept_cmd) {
|
|
/* use our builtin popup button */
|
|
|
|
/* (popup|popupkey|popupmouse)[+-X+-Y][:timeout] */
|
|
|
|
int ret, timeout = 120;
|
|
int x = -64000, y = -64000;
|
|
char *p, *mode;
|
|
char *userhost = ident_username(client);
|
|
|
|
/* extract timeout */
|
|
if ((p = strchr(accept_cmd, ':')) != NULL) {
|
|
int in;
|
|
if (sscanf(p+1, "%d", &in) == 1) {
|
|
timeout = in;
|
|
}
|
|
}
|
|
/* extract geometry */
|
|
if ((p = strpbrk(accept_cmd, "+-")) != NULL) {
|
|
ugly_geom(p, &x, &y);
|
|
}
|
|
|
|
/* find mode: mouse, key, or both */
|
|
if (strstr(accept_cmd, "popupmouse") == accept_cmd) {
|
|
mode = "mouse_only";
|
|
} else if (strstr(accept_cmd, "popupkey") == accept_cmd) {
|
|
mode = "key_only";
|
|
} else {
|
|
mode = "both";
|
|
}
|
|
|
|
if (dpy == NULL && use_dpy && strstr(use_dpy, "WAIT:") ==
|
|
use_dpy) {
|
|
rfbLog("accept_client: warning allowing client under conditions:\n");
|
|
rfbLog(" -display WAIT:, dpy == NULL, -accept popup.\n");
|
|
rfbLog(" There will be another popup.\n");
|
|
return 1;
|
|
}
|
|
|
|
rfbLog("accept_client: using builtin popup for: %s\n", addr);
|
|
if ((ret = ugly_window(addr, userhost, x, y, timeout,
|
|
mode, 1))) {
|
|
free(userhost);
|
|
if (ret == 2) {
|
|
rfbLog("accept_client: viewonly: %s\n", addr);
|
|
client->viewOnly = TRUE;
|
|
}
|
|
rfbLog("accept_client: popup accepted: %s\n", addr);
|
|
return 1;
|
|
} else {
|
|
free(userhost);
|
|
rfbLog("accept_client: popup rejected: %s\n", addr);
|
|
return 0;
|
|
}
|
|
|
|
} else if (!strcmp(accept_cmd, "xmessage")) {
|
|
/* make our own command using xmessage(1) */
|
|
|
|
if (view_only) {
|
|
sprintf(xmessage, "xmessage -buttons yes:0,no:2 -center"
|
|
" 'x11vnc: accept connection from %s?'", addr);
|
|
} else {
|
|
sprintf(xmessage, "xmessage -buttons yes:0,no:2,"
|
|
"view-only:3 -center" " 'x11vnc: accept connection"
|
|
" from %s?'", addr);
|
|
action = "yes:0,no:*,view:3";
|
|
}
|
|
cmd = xmessage;
|
|
|
|
} else {
|
|
/* use the user supplied command: */
|
|
|
|
cmd = accept_cmd;
|
|
|
|
/* extract any action prefix: yes:N,no:M,view:K */
|
|
if (strstr(accept_cmd, "yes:") == accept_cmd) {
|
|
char *p;
|
|
if ((p = strpbrk(accept_cmd, " \t")) != NULL) {
|
|
int i;
|
|
cmd = p;
|
|
p = accept_cmd;
|
|
for (i=0; i<200; i++) {
|
|
if (*p == ' ' || *p == '\t') {
|
|
xmessage[i] = '\0';
|
|
break;
|
|
}
|
|
xmessage[i] = *p;
|
|
p++;
|
|
}
|
|
xmessage[200-1] = '\0';
|
|
action = xmessage;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cmd) {
|
|
int rc;
|
|
|
|
rfbLog("accept_client: using cmd for: %s\n", addr);
|
|
rc = run_user_command(cmd, client, "accept", NULL, 0, NULL);
|
|
|
|
if (action) {
|
|
int result;
|
|
|
|
if (rc < 0) {
|
|
rfbLog("accept_client: cannot use negative "
|
|
"rc: %d, action %s\n", rc, action);
|
|
result = 2;
|
|
} else {
|
|
result = action_match(action, rc);
|
|
}
|
|
|
|
if (result == 1) {
|
|
rc = 0;
|
|
} else if (result == 2) {
|
|
rc = 1;
|
|
} else if (result == 3) {
|
|
rc = 0;
|
|
rfbLog("accept_client: viewonly: %s\n", addr);
|
|
client->viewOnly = TRUE;
|
|
} else {
|
|
rc = 1; /* NOTREACHED */
|
|
}
|
|
}
|
|
|
|
if (rc == 0) {
|
|
rfbLog("accept_client: accepted: %s\n", addr);
|
|
return 1;
|
|
} else {
|
|
rfbLog("accept_client: rejected: %s\n", addr);
|
|
return 0;
|
|
}
|
|
} else {
|
|
rfbLog("accept_client: no command, rejecting %s\n", addr);
|
|
return 0;
|
|
}
|
|
|
|
/* return 0; NOTREACHED */
|
|
}
|
|
|
|
/*
|
|
* For the -connect <file> option: periodically read the file looking for
|
|
* a connect string. If one is found set client_connect to it.
|
|
*/
|
|
static void check_connect_file(char *file) {
|
|
FILE *in;
|
|
char line[VNC_CONNECT_MAX], host[VNC_CONNECT_MAX];
|
|
static int first_warn = 1, truncate_ok = 1;
|
|
static time_t last_time = 0;
|
|
time_t now = time(NULL);
|
|
|
|
if (last_time == 0) {
|
|
last_time = now;
|
|
}
|
|
if (now - last_time < 1) {
|
|
/* check only once a second */
|
|
return;
|
|
}
|
|
last_time = now;
|
|
|
|
if (! truncate_ok) {
|
|
/* check if permissions changed */
|
|
if (access(file, W_OK) == 0) {
|
|
truncate_ok = 1;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
in = fopen(file, "r");
|
|
if (in == NULL) {
|
|
if (first_warn) {
|
|
rfbLog("check_connect_file: fopen failure: %s\n", file);
|
|
rfbLogPerror("fopen");
|
|
first_warn = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (fgets(line, VNC_CONNECT_MAX, in) != NULL) {
|
|
if (sscanf(line, "%s", host) == 1) {
|
|
if (strlen(host) > 0) {
|
|
char *str = strdup(host);
|
|
if (strlen(str) > 38) {
|
|
char trim[100];
|
|
trim[0] = '\0';
|
|
strncat(trim, str, 38);
|
|
rfbLog("read connect file: %s ...\n",
|
|
trim);
|
|
} else {
|
|
rfbLog("read connect file: %s\n", str);
|
|
}
|
|
if (!strcmp(str, "cmd=stop") &&
|
|
dnowx() < 3.0) {
|
|
rfbLog("ignoring stale cmd=stop\n");
|
|
} else {
|
|
client_connect = str;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
fclose(in);
|
|
|
|
/* truncate file */
|
|
in = fopen(file, "w");
|
|
if (in != NULL) {
|
|
fclose(in);
|
|
} else {
|
|
/* disable if we cannot truncate */
|
|
rfbLog("check_connect_file: could not truncate %s, "
|
|
"disabling checking.\n", file);
|
|
truncate_ok = 0;
|
|
}
|
|
}
|
|
|
|
static int socks5_proxy(char *host, int port, int sock) {
|
|
unsigned char buf[512], tmp[2];
|
|
char reply[512];
|
|
int len, n, i, j = 0;
|
|
|
|
memset(buf, 0, 512);
|
|
memset(reply, 0, 512);
|
|
|
|
buf[0] = 0x5;
|
|
buf[1] = 0x1;
|
|
buf[2] = 0x0;
|
|
|
|
write(sock, buf, 3);
|
|
|
|
n = read(sock, buf, 2);
|
|
|
|
if (n != 2) {
|
|
rfbLog("socks5_proxy: read error: %d\n", n);
|
|
close(sock);
|
|
return 0;
|
|
}
|
|
if (buf[0] != 0x5 || buf[1] != 0x0) {
|
|
rfbLog("socks5_proxy: handshake error: %d %d\n", (int) buf[0], (int) buf[1]);
|
|
close(sock);
|
|
return 0;
|
|
}
|
|
|
|
buf[0] = 0x5;
|
|
buf[1] = 0x1;
|
|
buf[2] = 0x0;
|
|
buf[3] = 0x3;
|
|
|
|
buf[4] = (unsigned char) strlen(host);
|
|
strcat((char *) buf+5, host);
|
|
|
|
len = 5 + strlen(host);
|
|
|
|
buf[len] = (unsigned char) (port >> 8);
|
|
buf[len+1] = (unsigned char) (port & 0xff);
|
|
|
|
write(sock, buf, len+2);
|
|
|
|
for (i=0; i<4; i++) {
|
|
int n;
|
|
n = read(sock, tmp, 1);
|
|
j++;
|
|
if (n < 0) {
|
|
if (errno != EINTR) {
|
|
break;
|
|
} else {
|
|
i--;
|
|
if (j > 100) {
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
if (n == 0) {
|
|
break;
|
|
}
|
|
reply[i] = tmp[0];
|
|
}
|
|
if (reply[3] == 0x1) {
|
|
read(sock, reply+4, 4 + 2);
|
|
} else if (reply[3] == 0x3) {
|
|
n = read(sock, tmp, 1);
|
|
reply[4] = tmp[0];
|
|
read(sock, reply+5, (int) reply[4] + 2);
|
|
} else if (reply[3] == 0x4) {
|
|
read(sock, reply+4, 16 + 2);
|
|
}
|
|
|
|
if (0) {
|
|
int i;
|
|
for (i=0; i<len+2; i++) {
|
|
fprintf(stderr, "b[%d]: %d\n", i, (int) buf[i]);
|
|
}
|
|
for (i=0; i<len+2; i++) {
|
|
fprintf(stderr, "r[%d]: %d\n", i, (int) reply[i]);
|
|
}
|
|
}
|
|
if (reply[0] == 0x5 && reply[1] == 0x0 && reply[2] == 0x0) {
|
|
rfbLog("SOCKS5 connect OK to %s:%d sock=%d\n", host, port, sock);
|
|
return 1;
|
|
} else {
|
|
rfbLog("SOCKS5 error to %s:%d sock=%d\n", host, port, sock);
|
|
close(sock);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int socks_proxy(char *host, int port, int sock) {
|
|
unsigned char buf[512], tmp[2];
|
|
char reply[16];
|
|
int socks4a = 0, len, i, j = 0, d1, d2, d3, d4;
|
|
|
|
memset(buf, 0, 512);
|
|
|
|
buf[0] = 0x4;
|
|
buf[1] = 0x1;
|
|
buf[2] = (unsigned char) (port >> 8);
|
|
buf[3] = (unsigned char) (port & 0xff);
|
|
|
|
|
|
if (strlen(host) > 256) {
|
|
rfbLog("socks_proxy: hostname too long: %s\n", host);
|
|
close(sock);
|
|
return 0;
|
|
}
|
|
|
|
if (!strcmp(host, "localhost") || !strcmp(host, "127.0.0.1")) {
|
|
buf[4] = 127;
|
|
buf[5] = 0;
|
|
buf[6] = 0;
|
|
buf[7] = 1;
|
|
} else if (sscanf(host, "%d.%d.%d.%d", &d1, &d2, &d3, &d4) == 4) {
|
|
buf[4] = (unsigned char) d1;
|
|
buf[5] = (unsigned char) d2;
|
|
buf[6] = (unsigned char) d3;
|
|
buf[7] = (unsigned char) d4;
|
|
} else {
|
|
buf[4] = 0x0;
|
|
buf[5] = 0x0;
|
|
buf[6] = 0x0;
|
|
buf[7] = 0x3;
|
|
socks4a = 1;
|
|
}
|
|
len = 8;
|
|
|
|
strcat((char *)buf+8, "nobody");
|
|
len += strlen("nobody") + 1;
|
|
|
|
if (socks4a) {
|
|
strcat((char *) buf+8+strlen("nobody") + 1, host);
|
|
len += strlen(host) + 1;
|
|
}
|
|
|
|
write(sock, buf, len);
|
|
|
|
for (i=0; i<8; i++) {
|
|
int n;
|
|
n = read(sock, tmp, 1);
|
|
j++;
|
|
if (n < 0) {
|
|
if (errno != EINTR) {
|
|
break;
|
|
} else {
|
|
i--;
|
|
if (j > 100) {
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
if (n == 0) {
|
|
break;
|
|
}
|
|
reply[i] = tmp[0];
|
|
}
|
|
if (0) {
|
|
int i;
|
|
for (i=0; i<len; i++) {
|
|
fprintf(stderr, "b[%d]: %d\n", i, (int) buf[i]);
|
|
}
|
|
for (i=0; i<8; i++) {
|
|
fprintf(stderr, "r[%d]: %d\n", i, (int) reply[i]);
|
|
}
|
|
}
|
|
if (reply[0] == 0x0 && reply[1] == 0x5a) {
|
|
if (socks4a) {
|
|
rfbLog("SOCKS4a connect OK to %s:%d sock=%d\n", host, port, sock);
|
|
} else {
|
|
rfbLog("SOCKS4 connect OK to %s:%d sock=%d\n", host, port, sock);
|
|
}
|
|
return 1;
|
|
} else {
|
|
if (socks4a) {
|
|
rfbLog("SOCKS4a error to %s:%d sock=%d\n", host, port, sock);
|
|
} else {
|
|
rfbLog("SOCKS4 error to %s:%d sock=%d\n", host, port, sock);
|
|
}
|
|
close(sock);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
#define PXY_HTTP 1
|
|
#define PXY_GET 2
|
|
#define PXY_SOCKS 3
|
|
#define PXY_SOCKS5 4
|
|
#define PXY_SSH 5
|
|
#define PXY 3
|
|
|
|
static int pxy_get_sock;
|
|
|
|
static int pconnect(int psock, char *host, int port, int type, char *http_path, char *gethost, int getport) {
|
|
char reply[4096];
|
|
int i, ok, len;
|
|
char *req;
|
|
|
|
pxy_get_sock = -1;
|
|
|
|
if (type == PXY_SOCKS) {
|
|
return socks_proxy(host, port, psock);
|
|
}
|
|
if (type == PXY_SOCKS5) {
|
|
return socks5_proxy(host, port, psock);
|
|
}
|
|
if (type == PXY_SSH) {
|
|
return 1;
|
|
}
|
|
|
|
len = strlen("CONNECT ") + strlen(host);
|
|
if (type == PXY_GET) {
|
|
len += strlen(http_path) + strlen(gethost);
|
|
len += strlen("host=") + 1 + strlen("port=") + 1 + 1;
|
|
}
|
|
len += 1 + 20 + strlen("HTTP/1.1\r\n") + 1;
|
|
|
|
req = (char *)malloc(len);
|
|
|
|
if (type == PXY_GET) {
|
|
int noquery = 0;
|
|
char *t = strstr(http_path, "__END__");
|
|
if (t) {
|
|
noquery = 1;
|
|
*t = '\0';
|
|
}
|
|
|
|
if (noquery) {
|
|
sprintf(req, "GET %s HTTP/1.1\r\n", http_path);
|
|
} else {
|
|
sprintf(req, "GET %shost=%s&port=%d HTTP/1.1\r\n", http_path, host, port);
|
|
}
|
|
} else {
|
|
sprintf(req, "CONNECT %s:%d HTTP/1.1\r\n", host, port);
|
|
}
|
|
rfbLog("http proxy: %s", req);
|
|
write(psock, req, strlen(req));
|
|
|
|
if (type == PXY_GET) {
|
|
char *t = "Connection: close\r\n";
|
|
write(psock, t, strlen(t));
|
|
}
|
|
|
|
if (type == PXY_GET) {
|
|
sprintf(req, "Host: %s:%d\r\n", gethost, getport);
|
|
rfbLog("http proxy: %s", req);
|
|
sprintf(req, "Host: %s:%d\r\n\r\n", gethost, getport);
|
|
} else {
|
|
sprintf(req, "Host: %s:%d\r\n", host, port);
|
|
rfbLog("http proxy: %s", req);
|
|
sprintf(req, "Host: %s:%d\r\n\r\n", host, port);
|
|
}
|
|
|
|
write(psock, req, strlen(req));
|
|
|
|
ok = 0;
|
|
reply[0] = '\0';
|
|
|
|
for (i=0; i<4096; i++) {
|
|
int n;
|
|
req[0] = req[1] = '\0';
|
|
n = read(psock, req, 1);
|
|
if (n < 0) {
|
|
if (errno != EINTR) {
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
if (n == 0) {
|
|
break;
|
|
}
|
|
strcat(reply, req);
|
|
if (strstr(reply, "\r\n\r\n")) {
|
|
if (strstr(reply, "HTTP/") == reply) {
|
|
char *q = strchr(reply, ' ');
|
|
if (q) {
|
|
q++;
|
|
if (q[0] == '2' && q[1] == '0' && q[2] == '0' && q[3] == ' ') {
|
|
ok = 1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (type == PXY_GET) {
|
|
char *t1 = strstr(reply, "VNC-IP-Port: ");
|
|
char *t2 = strstr(reply, "VNC-Host-Port: ");
|
|
char *s, *newhost = NULL;
|
|
int newport = 0;
|
|
fprintf(stderr, "%s\n", reply);
|
|
if (t1) {
|
|
t1 += strlen("VNC-IP-Port: ");
|
|
s = strstr(t1, ":");
|
|
if (s) {
|
|
*s = '\0';
|
|
newhost = strdup(t1);
|
|
newport = atoi(s+1);
|
|
}
|
|
} else if (t2) {
|
|
t2 += strlen("VNC-Host-Port: ");
|
|
s = strstr(t2, ":");
|
|
if (s) {
|
|
*s = '\0';
|
|
newhost = strdup(t2);
|
|
newport = atoi(s+1);
|
|
}
|
|
}
|
|
if (newhost && newport > 0) {
|
|
rfbLog("proxy GET reconnect to: %s:%d\n", newhost, newport);
|
|
pxy_get_sock = rfbConnectToTcpAddr(newhost, newport);
|
|
}
|
|
}
|
|
free(req);
|
|
|
|
return ok;
|
|
}
|
|
|
|
static int proxy_connect(char *host, int port) {
|
|
char *p, *q, *str;
|
|
int i, n, pxy[PXY],pxy_p[PXY];
|
|
int psock = -1;
|
|
char *pxy_h[PXY], *pxy_g[PXY];
|
|
|
|
if (! connect_proxy) {
|
|
return -1;
|
|
}
|
|
str = strdup(connect_proxy);
|
|
|
|
for (i=0; i<PXY; i++) {
|
|
pxy[i] = 0;
|
|
pxy_p[i] = 0;
|
|
pxy_h[i] = NULL;
|
|
pxy_g[i] = NULL;
|
|
}
|
|
|
|
n = 0;
|
|
p = str;
|
|
while (p) {
|
|
char *hp, *c, *s = NULL;
|
|
|
|
q = strchr(p, ',');
|
|
if (q) {
|
|
*q = '\0';
|
|
}
|
|
|
|
if (n==0) fprintf(stderr, "\n");
|
|
rfbLog("proxy_connect[%d]: %s\n", n+1, p);
|
|
|
|
pxy[n] = 0;
|
|
pxy_p[n] = 0;
|
|
pxy_h[n] = NULL;
|
|
pxy_g[n] = NULL;
|
|
|
|
if (strstr(p, "socks://") == p) {
|
|
hp = strstr(p, "://") + 3;
|
|
pxy[n] = PXY_SOCKS;
|
|
} else if (strstr(p, "socks4://") == p) {
|
|
hp = strstr(p, "://") + 3;
|
|
pxy[n] = PXY_SOCKS;
|
|
} else if (strstr(p, "socks5://") == p) {
|
|
hp = strstr(p, "://") + 3;
|
|
pxy[n] = PXY_SOCKS5;
|
|
} else if (strstr(p, "ssh://") == p) {
|
|
if (n != 0) {
|
|
rfbLog("ssh:// proxy must be the first one\n");
|
|
clean_up_exit(1);
|
|
}
|
|
hp = strstr(p, "://") + 3;
|
|
pxy[n] = PXY_SSH;
|
|
} else if (strstr(p, "http://") == p) {
|
|
hp = strstr(p, "://") + 3;
|
|
pxy[n] = PXY_HTTP;
|
|
} else if (strstr(p, "https://") == p) {
|
|
hp = strstr(p, "://") + 3;
|
|
pxy[n] = PXY_HTTP;
|
|
} else {
|
|
hp = p;
|
|
pxy[n] = PXY_HTTP;
|
|
}
|
|
c = strstr(hp, ":");
|
|
if (!c && pxy[n] == PXY_SSH) {
|
|
char *hp2 = (char *) malloc(strlen(hp) + 5);
|
|
sprintf(hp2, "%s:1", hp);
|
|
hp = hp2;
|
|
c = strstr(hp, ":");
|
|
}
|
|
if (!c) {
|
|
pxy[n] = 0;
|
|
if (q) {
|
|
*q = ',';
|
|
p = q + 1;
|
|
} else {
|
|
p = NULL;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (pxy[n] == PXY_HTTP) {
|
|
s = strstr(c, "/");
|
|
if (s) {
|
|
pxy[n] = PXY_GET;
|
|
pxy_g[n] = strdup(s);
|
|
*s = '\0';
|
|
}
|
|
}
|
|
pxy_p[n] = atoi(c+1);
|
|
|
|
if (pxy_p[n] <= 0) {
|
|
pxy[n] = 0;
|
|
pxy_p[n] = 0;
|
|
if (q) {
|
|
*q = ',';
|
|
p = q + 1;
|
|
} else {
|
|
p = NULL;
|
|
}
|
|
continue;
|
|
}
|
|
*c = '\0';
|
|
pxy_h[n] = strdup(hp);
|
|
|
|
if (++n >= PXY) {
|
|
break;
|
|
}
|
|
|
|
if (q) {
|
|
*q = ',';
|
|
p = q + 1;
|
|
} else {
|
|
p = NULL;
|
|
}
|
|
}
|
|
free(str);
|
|
|
|
if (!n) {
|
|
psock = -1;
|
|
goto pxy_clean;
|
|
}
|
|
|
|
if (pxy[0] == PXY_SSH) {
|
|
int rc, len = 0;
|
|
char *cmd, *ssh;
|
|
int sport = find_free_port(7300, 8000);
|
|
if (getenv("SSH")) {
|
|
ssh = getenv("SSH");
|
|
} else {
|
|
ssh = "ssh";
|
|
}
|
|
len = 200 + strlen(ssh) + strlen(pxy_h[0]) + strlen(host);
|
|
cmd = (char *) malloc(len);
|
|
if (n == 1) {
|
|
if (pxy_p[0] <= 1) {
|
|
sprintf(cmd, "%s -f -L '%d:%s:%d' '%s' 'sleep 20'", ssh, sport, host, port, pxy_h[0]);
|
|
} else {
|
|
sprintf(cmd, "%s -f -p %d -L '%d:%s:%d' '%s' 'sleep 20'", ssh, pxy_p[0], sport, host, port, pxy_h[0]);
|
|
}
|
|
} else {
|
|
if (pxy_p[0] <= 1) {
|
|
sprintf(cmd, "%s -f -L '%d:%s:%d' '%s' 'sleep 20'", ssh, sport, pxy_h[1], pxy_p[1], pxy_h[0]);
|
|
} else {
|
|
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]);
|
|
}
|
|
}
|
|
if (no_external_cmds || !cmd_ok("ssh")) {
|
|
rfbLogEnable(1);
|
|
rfbLog("cannot run external commands in -nocmds mode:\n");
|
|
rfbLog(" \"%s\"\n", cmd);
|
|
rfbLog(" exiting.\n");
|
|
clean_up_exit(1);
|
|
}
|
|
close_exec_fds();
|
|
fprintf(stderr, "\n");
|
|
rfbLog("running: %s\n", cmd);
|
|
rc = system(cmd);
|
|
free(cmd);
|
|
if (rc != 0) {
|
|
psock = -1;
|
|
goto pxy_clean;
|
|
}
|
|
psock = rfbConnectToTcpAddr("localhost", sport);
|
|
|
|
} else {
|
|
psock = rfbConnectToTcpAddr(pxy_h[0], pxy_p[0]);
|
|
}
|
|
|
|
if (psock < 0) {
|
|
psock = -1;
|
|
goto pxy_clean;
|
|
}
|
|
rfbLog("opened socket to proxy: %s:%d\n", pxy_h[0], pxy_p[0]);
|
|
|
|
if (n >= 2) {
|
|
if (! pconnect(psock, pxy_h[1], pxy_p[1], pxy[0], pxy_g[0], pxy_h[0], pxy_p[0])) {
|
|
close(psock); psock = -1; goto pxy_clean;
|
|
}
|
|
if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
|
|
|
|
if (n >= 3) {
|
|
if (! pconnect(psock, pxy_h[2], pxy_p[2], pxy[1], pxy_g[1], pxy_h[1], pxy_p[1])) {
|
|
close(psock); psock = -1; goto pxy_clean;
|
|
}
|
|
if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
|
|
if (! pconnect(psock, host, port, pxy[2], pxy_g[2], pxy_h[2], pxy_p[2])) {
|
|
close(psock); psock = -1; goto pxy_clean;
|
|
}
|
|
if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
|
|
|
|
} else {
|
|
if (! pconnect(psock, host, port, pxy[1], pxy_g[1], pxy_h[1], pxy_p[1])) {
|
|
close(psock); psock = -1; goto pxy_clean;
|
|
}
|
|
if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
|
|
}
|
|
} else {
|
|
if (! pconnect(psock, host, port, pxy[0], pxy_g[0], pxy_h[0], pxy_p[0])) {
|
|
close(psock); psock = -1; goto pxy_clean;
|
|
}
|
|
if (pxy_get_sock >= 0) {close(psock); psock = pxy_get_sock;}
|
|
}
|
|
|
|
pxy_clean:
|
|
for (i=0; i < PXY; i++) {
|
|
if (pxy_h[i] != NULL) {
|
|
free(pxy_h[i]);
|
|
}
|
|
if (pxy_g[i] != NULL) {
|
|
free(pxy_g[i]);
|
|
}
|
|
}
|
|
|
|
return psock;
|
|
}
|
|
|
|
char *get_repeater_string(char *str, int *len) {
|
|
int pren, which = 0;
|
|
int prestring_len = 0;
|
|
char *prestring = NULL, *ptmp = NULL;
|
|
char *equals = strchr(str, '=');
|
|
char *plus = strrchr(str, '+');
|
|
|
|
*len = 0;
|
|
if (!plus || !equals) {
|
|
return NULL;
|
|
}
|
|
|
|
*plus = '\0';
|
|
if (strstr(str, "repeater=") == str) {
|
|
/* ultravnc repeater http://www.uvnc.com/addons/repeater.html */
|
|
prestring_len = 250;
|
|
ptmp = (char *) calloc(prestring_len+1, 1);
|
|
snprintf(ptmp, 250, "%s", str + strlen("repeater="));
|
|
which = 1;
|
|
} else if (strstr(str, "pre=") == str) {
|
|
prestring_len = strlen(str + strlen("pre="));
|
|
ptmp = (char *) calloc(prestring_len+1, 1);
|
|
snprintf(ptmp, prestring_len+1, "%s", str + strlen("pre="));
|
|
which = 2;
|
|
} else if (sscanf(str, "pre%d=", &pren) == 1) {
|
|
if (pren > 0 && pren <= 16384) {
|
|
prestring_len = pren;
|
|
ptmp = (char *) calloc(prestring_len+1, 1);
|
|
snprintf(prestring, prestring_len, "%s", equals+1);
|
|
which = 3;
|
|
}
|
|
}
|
|
if (ptmp != NULL) {
|
|
int i, k = 0;
|
|
char *p = ptmp;
|
|
prestring = (char *)calloc(prestring_len+1, 1);
|
|
/* translate \n to newline, etc. */
|
|
for (i=0; i < prestring_len; i++) {
|
|
if (i < prestring_len-1 && *(p+i) == '\\') {
|
|
if (*(p+i+1) == 'r') {
|
|
prestring[k++] = '\r'; i++;
|
|
} else if (*(p+i+1) == 'n') {
|
|
prestring[k++] = '\n'; i++;
|
|
} else if (*(p+i+1) == 't') {
|
|
prestring[k++] = '\t'; i++;
|
|
} else if (*(p+i+1) == 'a') {
|
|
prestring[k++] = '\a'; i++;
|
|
} else if (*(p+i+1) == 'b') {
|
|
prestring[k++] = '\b'; i++;
|
|
} else if (*(p+i+1) == 'v') {
|
|
prestring[k++] = '\v'; i++;
|
|
} else if (*(p+i+1) == 'f') {
|
|
prestring[k++] = '\f'; i++;
|
|
} else if (*(p+i+1) == '\\') {
|
|
prestring[k++] = '\\'; i++;
|
|
} else if (*(p+i+1) == 'c') {
|
|
prestring[k++] = ','; i++;
|
|
} else {
|
|
prestring[k++] = *(p+i);
|
|
}
|
|
} else {
|
|
prestring[k++] = *(p+i);
|
|
}
|
|
}
|
|
if (which == 2) {
|
|
prestring_len = k;
|
|
}
|
|
if (!quiet) {
|
|
rfbLog("-connect prestring: '%s'\n", prestring);
|
|
}
|
|
free(ptmp);
|
|
}
|
|
*plus = '+';
|
|
|
|
*len = prestring_len;
|
|
return prestring;
|
|
}
|
|
|
|
/*
|
|
* Do a reverse connect for a single "host" or "host:port"
|
|
*/
|
|
|
|
extern int ssl_client_mode;
|
|
|
|
static int do_reverse_connect(char *str_in) {
|
|
rfbClientPtr cl;
|
|
char *host, *p, *str = str_in, *s = NULL;
|
|
char *prestring = NULL;
|
|
int prestring_len = 0;
|
|
int rport = 5500, len = strlen(str);
|
|
|
|
if (len < 1) {
|
|
return 0;
|
|
}
|
|
if (len > 1024) {
|
|
rfbLog("reverse_connect: string too long: %d bytes\n", len);
|
|
return 0;
|
|
}
|
|
if (!screen) {
|
|
rfbLog("reverse_connect: screen not setup yet.\n");
|
|
return 0;
|
|
}
|
|
if (unixpw_in_progress) return 0;
|
|
|
|
/* look for repeater pre-string */
|
|
if (strchr(str, '=') && strrchr(str, '+')
|
|
&& (strstr(str, "pre") == str || strstr(str, "repeater=") == str)) {
|
|
prestring = get_repeater_string(str, &prestring_len);
|
|
str = strrchr(str, '+') + 1;
|
|
} else if (strrchr(str, '+') && strstr(str, "repeater://") == str) {
|
|
/* repeater://host:port+string */
|
|
/* repeater=string+host:port */
|
|
char *plus = strrchr(str, '+');
|
|
str = (char *) malloc(strlen(str_in)+1);
|
|
s = str;
|
|
*plus = '\0';
|
|
sprintf(str, "repeater=%s+%s", plus+1, str_in + strlen("repeater://"));
|
|
prestring = get_repeater_string(str, &prestring_len);
|
|
str = strrchr(str, '+') + 1;
|
|
*plus = '+';
|
|
}
|
|
|
|
/* copy in to host */
|
|
host = (char *) malloc(len+1);
|
|
if (! host) {
|
|
rfbLog("reverse_connect: could not malloc string %d\n", len);
|
|
return 0;
|
|
}
|
|
strncpy(host, str, len);
|
|
host[len] = '\0';
|
|
|
|
/* extract port, if any */
|
|
if ((p = strchr(host, ':')) != NULL) {
|
|
rport = atoi(p+1);
|
|
if (rport < 0) {
|
|
rport = -rport;
|
|
} else if (rport < 20) {
|
|
rport = 5500 + rport;
|
|
}
|
|
*p = '\0';
|
|
}
|
|
|
|
if (use_openssl) {
|
|
int vncsock;
|
|
if (connect_proxy) {
|
|
vncsock = proxy_connect(host, rport);
|
|
} else {
|
|
vncsock = rfbConnectToTcpAddr(host, rport);
|
|
}
|
|
if (vncsock < 0) {
|
|
rfbLog("reverse_connect: failed to connect to: %s\n", str);
|
|
return 0;
|
|
}
|
|
if (prestring != NULL) {
|
|
write(vncsock, prestring, prestring_len);
|
|
free(prestring);
|
|
}
|
|
#define OPENSSL_REVERSE 4
|
|
openssl_init(1);
|
|
accept_openssl(OPENSSL_REVERSE, vncsock);
|
|
openssl_init(0);
|
|
free(host);
|
|
return 1;
|
|
}
|
|
if (use_stunnel) {
|
|
if(strcmp(host, "localhost") && strcmp(host, "127.0.0.1")) {
|
|
if (!getenv("STUNNEL_DISABLE_LOCALHOST")) {
|
|
rfbLog("reverse_connect: error host not localhost in -stunnel mode.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (unixpw) {
|
|
int is_localhost = 0, user_disabled_it = 0;
|
|
|
|
if(!strcmp(host, "localhost") || !strcmp(host, "127.0.0.1")) {
|
|
is_localhost = 1;
|
|
}
|
|
if (getenv("UNIXPW_DISABLE_LOCALHOST")) {
|
|
user_disabled_it = 1;
|
|
}
|
|
|
|
if (! is_localhost) {
|
|
if (user_disabled_it) {
|
|
rfbLog("reverse_connect: warning disabling localhost constraint in -unixpw\n");
|
|
} else {
|
|
rfbLog("reverse_connect: error not localhost in -unixpw\n");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (connect_proxy != NULL) {
|
|
int sock = proxy_connect(host, rport);
|
|
if (sock >= 0) {
|
|
if (prestring != NULL) {
|
|
write(sock, prestring, prestring_len);
|
|
free(prestring);
|
|
}
|
|
cl = rfbNewClient(screen, sock);
|
|
} else {
|
|
return 0;
|
|
}
|
|
} else if (prestring != NULL) {
|
|
int sock = rfbConnectToTcpAddr(host, rport);
|
|
if (sock >= 0) {
|
|
write(sock, prestring, prestring_len);
|
|
free(prestring);
|
|
cl = rfbNewClient(screen, sock);
|
|
} else {
|
|
return 0;
|
|
}
|
|
} else {
|
|
cl = rfbReverseConnection(screen, host, rport);
|
|
}
|
|
|
|
free(host);
|
|
|
|
if (cl == NULL) {
|
|
if (quiet && connect_or_exit) {
|
|
rfbLogEnable(1);
|
|
}
|
|
rfbLog("reverse_connect: %s failed\n", str);
|
|
return 0;
|
|
} else {
|
|
rfbLog("reverse_connect: %s/%s OK\n", str, cl->host);
|
|
/* let's see if anyone complains: */
|
|
if (! getenv("X11VNC_REVERSE_CONNECTION_NO_AUTH")) {
|
|
rfbLog("reverse_connect: turning on auth for %s\n",
|
|
cl->host);
|
|
cl->reverseConnection = FALSE;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Break up comma separated list of hosts and call do_reverse_connect()
|
|
*/
|
|
void reverse_connect(char *str) {
|
|
char *p, *tmp;
|
|
int sleep_between_host = 300;
|
|
int sleep_min = 1500, sleep_max = 4500, n_max = 5;
|
|
int n, tot, t, dt = 100, cnt = 0;
|
|
int nclients0 = client_count;
|
|
|
|
if (unixpw_in_progress) return;
|
|
|
|
tmp = strdup(str);
|
|
|
|
p = strtok(tmp, ", \t\r\n");
|
|
while (p) {
|
|
if ((n = do_reverse_connect(p)) != 0) {
|
|
rfbPE(-1);
|
|
}
|
|
cnt += n;
|
|
|
|
p = strtok(NULL, ", \t\r\n");
|
|
if (p) {
|
|
t = 0;
|
|
while (t < sleep_between_host) {
|
|
usleep(dt * 1000);
|
|
rfbPE(-1);
|
|
t += dt;
|
|
}
|
|
}
|
|
}
|
|
free(tmp);
|
|
|
|
if (cnt == 0) {
|
|
if (connect_or_exit) {
|
|
rfbLogEnable(1);
|
|
rfbLog("exiting under -connect_or_exit\n");
|
|
if (gui_pid > 0) {
|
|
rfbLog("killing gui_pid %d\n", gui_pid);
|
|
kill(gui_pid, SIGTERM);
|
|
}
|
|
clean_up_exit(0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* XXX: we need to process some of the initial handshaking
|
|
* events, otherwise the client can get messed up (why??)
|
|
* so we send rfbProcessEvents() all over the place.
|
|
*/
|
|
|
|
n = cnt;
|
|
if (n >= n_max) {
|
|
n = n_max;
|
|
}
|
|
t = sleep_max - sleep_min;
|
|
tot = sleep_min + ((n-1) * t) / (n_max-1);
|
|
|
|
t = 0;
|
|
while (t < tot) {
|
|
rfbPE(-1);
|
|
rfbPE(-1);
|
|
usleep(dt * 1000);
|
|
t += dt;
|
|
}
|
|
if (connect_or_exit) {
|
|
if (client_count <= nclients0) {
|
|
for (t = 0; t < 10; t++) {
|
|
rfbPE(-1);
|
|
usleep(100 * 1000);
|
|
}
|
|
}
|
|
if (client_count <= nclients0) {
|
|
rfbLogEnable(1);
|
|
rfbLog("exiting under -connect_or_exit\n");
|
|
if (gui_pid > 0) {
|
|
rfbLog("killing gui_pid %d\n", gui_pid);
|
|
kill(gui_pid, SIGTERM);
|
|
}
|
|
clean_up_exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Routines for monitoring the VNC_CONNECT and X11VNC_REMOTE properties
|
|
* for changes. The vncconnect(1) will set it on our X display.
|
|
*/
|
|
void set_vnc_connect_prop(char *str) {
|
|
RAWFB_RET_VOID
|
|
#if !NO_X11
|
|
XChangeProperty(dpy, rootwin, vnc_connect_prop, XA_STRING, 8,
|
|
PropModeReplace, (unsigned char *)str, strlen(str));
|
|
#else
|
|
if (!str) {}
|
|
#endif /* NO_X11 */
|
|
}
|
|
|
|
void set_x11vnc_remote_prop(char *str) {
|
|
RAWFB_RET_VOID
|
|
#if !NO_X11
|
|
XChangeProperty(dpy, rootwin, x11vnc_remote_prop, XA_STRING, 8,
|
|
PropModeReplace, (unsigned char *)str, strlen(str));
|
|
#else
|
|
if (!str) {}
|
|
#endif /* NO_X11 */
|
|
}
|
|
|
|
void read_vnc_connect_prop(int nomsg) {
|
|
#if NO_X11
|
|
RAWFB_RET_VOID
|
|
if (!nomsg) {}
|
|
return;
|
|
#else
|
|
Atom type;
|
|
int format, slen, dlen;
|
|
unsigned long nitems = 0, bytes_after = 0;
|
|
unsigned char* data = NULL;
|
|
int db = 1;
|
|
|
|
vnc_connect_str[0] = '\0';
|
|
slen = 0;
|
|
|
|
if (! vnc_connect || vnc_connect_prop == None) {
|
|
/* not active or problem with VNC_CONNECT atom */
|
|
return;
|
|
}
|
|
RAWFB_RET_VOID
|
|
|
|
/* read the property value into vnc_connect_str: */
|
|
do {
|
|
if (XGetWindowProperty(dpy, DefaultRootWindow(dpy),
|
|
vnc_connect_prop, nitems/4, VNC_CONNECT_MAX/16, False,
|
|
AnyPropertyType, &type, &format, &nitems, &bytes_after,
|
|
&data) == Success) {
|
|
|
|
dlen = nitems * (format/8);
|
|
if (slen + dlen > VNC_CONNECT_MAX) {
|
|
/* too big */
|
|
rfbLog("warning: truncating large VNC_CONNECT"
|
|
" string > %d bytes.\n", VNC_CONNECT_MAX);
|
|
XFree_wr(data);
|
|
break;
|
|
}
|
|
memcpy(vnc_connect_str+slen, data, dlen);
|
|
slen += dlen;
|
|
vnc_connect_str[slen] = '\0';
|
|
XFree_wr(data);
|
|
}
|
|
} while (bytes_after > 0);
|
|
|
|
vnc_connect_str[VNC_CONNECT_MAX] = '\0';
|
|
if (! db || nomsg) {
|
|
;
|
|
} else {
|
|
rfbLog("read VNC_CONNECT: %s\n", vnc_connect_str);
|
|
}
|
|
#endif /* NO_X11 */
|
|
}
|
|
|
|
void read_x11vnc_remote_prop(int nomsg) {
|
|
#if NO_X11
|
|
RAWFB_RET_VOID
|
|
if (!nomsg) {}
|
|
return;
|
|
#else
|
|
Atom type;
|
|
int format, slen, dlen;
|
|
unsigned long nitems = 0, bytes_after = 0;
|
|
unsigned char* data = NULL;
|
|
int db = 1;
|
|
|
|
x11vnc_remote_str[0] = '\0';
|
|
slen = 0;
|
|
|
|
if (! vnc_connect || x11vnc_remote_prop == None) {
|
|
/* not active or problem with X11VNC_REMOTE atom */
|
|
return;
|
|
}
|
|
RAWFB_RET_VOID
|
|
|
|
/* read the property value into x11vnc_remote_str: */
|
|
do {
|
|
if (XGetWindowProperty(dpy, DefaultRootWindow(dpy),
|
|
x11vnc_remote_prop, nitems/4, X11VNC_REMOTE_MAX/16, False,
|
|
AnyPropertyType, &type, &format, &nitems, &bytes_after,
|
|
&data) == Success) {
|
|
|
|
dlen = nitems * (format/8);
|
|
if (slen + dlen > X11VNC_REMOTE_MAX) {
|
|
/* too big */
|
|
rfbLog("warning: truncating large X11VNC_REMOTE"
|
|
" string > %d bytes.\n", X11VNC_REMOTE_MAX);
|
|
XFree_wr(data);
|
|
break;
|
|
}
|
|
memcpy(x11vnc_remote_str+slen, data, dlen);
|
|
slen += dlen;
|
|
x11vnc_remote_str[slen] = '\0';
|
|
XFree_wr(data);
|
|
}
|
|
} while (bytes_after > 0);
|
|
|
|
x11vnc_remote_str[X11VNC_REMOTE_MAX] = '\0';
|
|
if (! db || nomsg) {
|
|
;
|
|
} else if (strstr(x11vnc_remote_str, "ans=stop:N/A,ans=quit:N/A,ans=")) {
|
|
;
|
|
} else if (strstr(x11vnc_remote_str, "qry=stop,quit,exit")) {
|
|
;
|
|
} else if (strstr(x11vnc_remote_str, "ack=") == x11vnc_remote_str) {
|
|
;
|
|
} else if (quiet && strstr(x11vnc_remote_str, "qry=ping") ==
|
|
x11vnc_remote_str) {
|
|
;
|
|
} else if (strstr(x11vnc_remote_str, "cmd=") &&
|
|
strstr(x11vnc_remote_str, "passwd")) {
|
|
rfbLog("read X11VNC_REMOTE: *\n");
|
|
} else if (strlen(x11vnc_remote_str) > 36) {
|
|
char trim[100];
|
|
trim[0] = '\0';
|
|
strncat(trim, x11vnc_remote_str, 36);
|
|
rfbLog("read X11VNC_REMOTE: %s ...\n", trim);
|
|
|
|
} else {
|
|
rfbLog("read X11VNC_REMOTE: %s\n", x11vnc_remote_str);
|
|
}
|
|
#endif /* NO_X11 */
|
|
}
|
|
|
|
/*
|
|
* check if client_connect has been set, if so make the reverse connections.
|
|
*/
|
|
static void send_client_connect(void) {
|
|
if (client_connect != NULL) {
|
|
char *str = client_connect;
|
|
if (strstr(str, "cmd=") == str || strstr(str, "qry=") == str) {
|
|
process_remote_cmd(client_connect, 0);
|
|
} else if (strstr(str, "ans=") == str
|
|
|| strstr(str, "aro=") == str) {
|
|
;
|
|
} else if (strstr(str, "ack=") == str) {
|
|
;
|
|
} else {
|
|
reverse_connect(client_connect);
|
|
}
|
|
free(client_connect);
|
|
client_connect = NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* monitor the various input methods
|
|
*/
|
|
void check_connect_inputs(void) {
|
|
|
|
if (unixpw_in_progress) return;
|
|
|
|
/* flush any already set: */
|
|
send_client_connect();
|
|
|
|
/* connect file: */
|
|
if (client_connect_file != NULL) {
|
|
check_connect_file(client_connect_file);
|
|
}
|
|
send_client_connect();
|
|
|
|
/* VNC_CONNECT property (vncconnect program) */
|
|
if (vnc_connect && *vnc_connect_str != '\0') {
|
|
client_connect = strdup(vnc_connect_str);
|
|
vnc_connect_str[0] = '\0';
|
|
}
|
|
send_client_connect();
|
|
|
|
/* X11VNC_REMOTE property */
|
|
if (vnc_connect && *x11vnc_remote_str != '\0') {
|
|
client_connect = strdup(x11vnc_remote_str);
|
|
x11vnc_remote_str[0] = '\0';
|
|
}
|
|
send_client_connect();
|
|
}
|
|
|
|
void check_gui_inputs(void) {
|
|
int i, gnmax = 0, n = 0, nfds;
|
|
int socks[ICON_MODE_SOCKS];
|
|
fd_set fds;
|
|
struct timeval tv;
|
|
char buf[X11VNC_REMOTE_MAX+1];
|
|
ssize_t nbytes;
|
|
|
|
if (unixpw_in_progress) return;
|
|
|
|
for (i=0; i<ICON_MODE_SOCKS; i++) {
|
|
if (icon_mode_socks[i] >= 0) {
|
|
socks[n++] = i;
|
|
if (icon_mode_socks[i] > gnmax) {
|
|
gnmax = icon_mode_socks[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! n) {
|
|
return;
|
|
}
|
|
|
|
FD_ZERO(&fds);
|
|
for (i=0; i<n; i++) {
|
|
FD_SET(icon_mode_socks[socks[i]], &fds);
|
|
}
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
nfds = select(gnmax+1, &fds, NULL, NULL, &tv);
|
|
if (nfds <= 0) {
|
|
return;
|
|
}
|
|
|
|
for (i=0; i<n; i++) {
|
|
int k, fd = icon_mode_socks[socks[i]];
|
|
char *p;
|
|
if (! FD_ISSET(fd, &fds)) {
|
|
continue;
|
|
}
|
|
for (k=0; k<=X11VNC_REMOTE_MAX; k++) {
|
|
buf[k] = '\0';
|
|
}
|
|
nbytes = read(fd, buf, X11VNC_REMOTE_MAX);
|
|
if (nbytes <= 0) {
|
|
close(fd);
|
|
icon_mode_socks[socks[i]] = -1;
|
|
continue;
|
|
}
|
|
|
|
p = strtok(buf, "\r\n");
|
|
while (p) {
|
|
if (strstr(p, "cmd=") == p ||
|
|
strstr(p, "qry=") == p) {
|
|
char *str = process_remote_cmd(p, 1);
|
|
if (! str) {
|
|
str = strdup("");
|
|
}
|
|
nbytes = write(fd, str, strlen(str));
|
|
write(fd, "\n", 1);
|
|
free(str);
|
|
if (nbytes < 0) {
|
|
close(fd);
|
|
icon_mode_socks[socks[i]] = -1;
|
|
break;
|
|
}
|
|
}
|
|
p = strtok(NULL, "\r\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static int turn_off_truecolor = 0;
|
|
|
|
static void turn_off_truecolor_ad(rfbClientPtr client) {
|
|
if (client) {}
|
|
if (turn_off_truecolor) {
|
|
rfbLog("turning off truecolor advertising.\n");
|
|
screen->serverFormat.trueColour = FALSE;
|
|
screen->displayHook = NULL;
|
|
screen->serverFormat.redShift = 0;
|
|
screen->serverFormat.greenShift = 0;
|
|
screen->serverFormat.blueShift = 0;
|
|
screen->serverFormat.redMax = 0;
|
|
screen->serverFormat.greenMax = 0;
|
|
screen->serverFormat.blueMax = 0;
|
|
turn_off_truecolor = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* some overrides for the local console text chat.
|
|
* could be useful in general for local helpers.
|
|
*/
|
|
|
|
rfbBool password_check_chat_helper(rfbClientPtr cl, const char* response, int len) {
|
|
if (cl != chat_window_client) {
|
|
rfbLog("invalid client during chat_helper login\n");
|
|
return FALSE;
|
|
} else {
|
|
if (!cl->host) {
|
|
rfbLog("empty cl->host during chat_helper login\n");
|
|
return FALSE;
|
|
}
|
|
if (strcmp(cl->host, "127.0.0.1")) {
|
|
rfbLog("invalid cl->host during chat_helper login: %s\n", cl->host);
|
|
return FALSE;
|
|
}
|
|
rfbLog("chat_helper login accepted\n");
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
enum rfbNewClientAction new_client_chat_helper(rfbClientPtr client) {
|
|
client->clientGoneHook = client_gone_chat_helper;
|
|
rfbLog("new chat helper\n");
|
|
return(RFB_CLIENT_ACCEPT);
|
|
}
|
|
|
|
void client_gone_chat_helper(rfbClientPtr client) {
|
|
rfbLog("finished chat helper\n");
|
|
chat_window_client = NULL;
|
|
}
|
|
|
|
/*
|
|
* libvncserver callback for when a new client connects
|
|
*/
|
|
enum rfbNewClientAction new_client(rfbClientPtr client) {
|
|
ClientData *cd;
|
|
|
|
last_event = last_input = time(NULL);
|
|
|
|
latest_client = client;
|
|
|
|
if (inetd) {
|
|
/*
|
|
* Set this so we exit as soon as connection closes,
|
|
* otherwise client_gone is only called after RFB_CLIENT_ACCEPT
|
|
*/
|
|
if (inetd_client == NULL) {
|
|
inetd_client = client;
|
|
client->clientGoneHook = client_gone;
|
|
}
|
|
}
|
|
|
|
clients_served++;
|
|
|
|
if (use_openssl || use_stunnel) {
|
|
if (! ssl_initialized) {
|
|
rfbLog("denying additional client: %s ssl not setup"
|
|
" yet.\n", client->host);
|
|
return(RFB_CLIENT_REFUSE);
|
|
}
|
|
}
|
|
if (unixpw_in_progress) {
|
|
rfbLog("denying additional client: %s during -unixpw login.\n",
|
|
client->host);
|
|
return(RFB_CLIENT_REFUSE);
|
|
}
|
|
if (connect_once) {
|
|
if (screen->dontDisconnect && screen->neverShared) {
|
|
if (! shared && accepted_client) {
|
|
rfbLog("denying additional client: %s\n",
|
|
client->host);
|
|
return(RFB_CLIENT_REFUSE);
|
|
}
|
|
}
|
|
}
|
|
if (! check_access(client->host)) {
|
|
rfbLog("denying client: %s does not match %s\n", client->host,
|
|
allow_list ? allow_list : "(null)" );
|
|
return(RFB_CLIENT_REFUSE);
|
|
}
|
|
|
|
client->clientData = (void *) calloc(sizeof(ClientData), 1);
|
|
cd = (ClientData *) client->clientData;
|
|
|
|
cd->client_port = get_remote_port(client->sock);
|
|
cd->server_port = get_local_port(client->sock);
|
|
cd->server_ip = get_local_host(client->sock);
|
|
cd->hostname = ip2host(client->host);
|
|
cd->username = strdup("");
|
|
cd->unixname = strdup("");
|
|
|
|
cd->input[0] = '-';
|
|
cd->login_viewonly = -1;
|
|
cd->login_time = time(NULL);
|
|
cd->ssl_helper_pid = 0;
|
|
|
|
if (use_openssl && openssl_last_helper_pid) {
|
|
cd->ssl_helper_pid = openssl_last_helper_pid;
|
|
openssl_last_helper_pid = 0;
|
|
}
|
|
|
|
if (! accept_client(client)) {
|
|
rfbLog("denying client: %s local user rejected connection.\n",
|
|
client->host);
|
|
rfbLog("denying client: accept_cmd=\"%s\"\n",
|
|
accept_cmd ? accept_cmd : "(null)" );
|
|
|
|
free_client_data(client);
|
|
|
|
return(RFB_CLIENT_REFUSE);
|
|
}
|
|
|
|
if (passwdfile) {
|
|
if (strstr(passwdfile, "read:") == passwdfile ||
|
|
strstr(passwdfile, "cmd:") == passwdfile) {
|
|
if (read_passwds(passwdfile)) {
|
|
install_passwds();
|
|
} else {
|
|
rfbLog("problem reading: %s\n", passwdfile);
|
|
clean_up_exit(1);
|
|
}
|
|
} else if (strstr(passwdfile, "custom:") == passwdfile) {
|
|
if (screen) {
|
|
screen->passwordCheck = custom_passwd_check;
|
|
}
|
|
}
|
|
}
|
|
|
|
cd->uid = clients_served;
|
|
|
|
client->clientGoneHook = client_gone;
|
|
|
|
if (client_count) {
|
|
speeds_net_rate_measured = 0;
|
|
speeds_net_latency_measured = 0;
|
|
}
|
|
client_count++;
|
|
|
|
last_keyboard_input = last_pointer_input = time(NULL);
|
|
|
|
if (no_autorepeat && client_count == 1 && ! view_only) {
|
|
/*
|
|
* first client, turn off X server autorepeat
|
|
* XXX handle dynamic change of view_only and per-client.
|
|
*/
|
|
autorepeat(0, 0);
|
|
}
|
|
#ifdef MACOSX
|
|
if (macosx_console && client_count == 1) {
|
|
macosxCG_refresh_callback_on();
|
|
}
|
|
#endif
|
|
if (use_solid_bg && client_count == 1) {
|
|
solid_bg(0);
|
|
}
|
|
|
|
if (pad_geometry) {
|
|
install_padded_fb(pad_geometry);
|
|
}
|
|
|
|
cd->timer = dnow();
|
|
cd->send_cmp_rate = 0.0;
|
|
cd->send_raw_rate = 0.0;
|
|
cd->latency = 0.0;
|
|
cd->cmp_bytes_sent = 0;
|
|
cd->raw_bytes_sent = 0;
|
|
|
|
accepted_client = 1;
|
|
last_client = time(NULL);
|
|
|
|
if (ncache) {
|
|
check_ncache(1, 0);
|
|
}
|
|
|
|
if (advertise_truecolor && indexed_color) {
|
|
int rs = 0, gs = 2, bs = 4;
|
|
int rm = 3, gm = 3, bm = 3;
|
|
if (bpp >= 24) {
|
|
rs = 0, gs = 8, bs = 16;
|
|
rm = 255, gm = 255, bm = 255;
|
|
} else if (bpp >= 16) {
|
|
rs = 0, gs = 5, bs = 10;
|
|
rm = 31, gm = 31, bm = 31;
|
|
}
|
|
rfbLog("advertising truecolor.\n");
|
|
if (getenv("ADVERT_BMSHIFT")) {
|
|
bm--;
|
|
}
|
|
|
|
client->format.trueColour = TRUE;
|
|
client->format.redShift = rs;
|
|
client->format.greenShift = gs;
|
|
client->format.blueShift = bs;
|
|
client->format.redMax = rm;
|
|
client->format.greenMax = gm;
|
|
client->format.blueMax = bm;
|
|
|
|
rfbSetTranslateFunction(client);
|
|
|
|
screen->serverFormat.trueColour = TRUE;
|
|
screen->serverFormat.redShift = rs;
|
|
screen->serverFormat.greenShift = gs;
|
|
screen->serverFormat.blueShift = bs;
|
|
screen->serverFormat.redMax = rm;
|
|
screen->serverFormat.greenMax = gm;
|
|
screen->serverFormat.blueMax = bm;
|
|
screen->displayHook = turn_off_truecolor_ad;
|
|
|
|
turn_off_truecolor = 1;
|
|
}
|
|
|
|
if (unixpw) {
|
|
unixpw_in_progress = 1;
|
|
unixpw_client = client;
|
|
unixpw_login_viewonly = 0;
|
|
|
|
unixpw_file_xfer_save = screen->permitFileTransfer;
|
|
screen->permitFileTransfer = FALSE;
|
|
unixpw_tightvnc_xfer_save = tightfilexfer;
|
|
tightfilexfer = 0;
|
|
#ifdef LIBVNCSERVER_WITH_TIGHTVNC_FILETRANSFER
|
|
rfbLog("rfbUnregisterTightVNCFileTransferExtension: 1\n");
|
|
rfbUnregisterTightVNCFileTransferExtension();
|
|
#endif
|
|
|
|
if (client->viewOnly) {
|
|
unixpw_login_viewonly = 1;
|
|
client->viewOnly = FALSE;
|
|
}
|
|
unixpw_last_try_time = time(NULL);
|
|
|
|
unixpw_screen(1);
|
|
unixpw_keystroke(0, 0, 1);
|
|
|
|
if (!unixpw_in_rfbPE) {
|
|
rfbLog("new client: %s in non-unixpw_in_rfbPE.\n",
|
|
client->host);
|
|
}
|
|
/* always put client on hold even if unixpw_in_rfbPE is true */
|
|
return(RFB_CLIENT_ON_HOLD);
|
|
}
|
|
|
|
return(RFB_CLIENT_ACCEPT);
|
|
}
|
|
|
|
void start_client_info_sock(char *host_port_cookie) {
|
|
char *host = NULL, *cookie = NULL, *p;
|
|
char *str = strdup(host_port_cookie);
|
|
int i, port, sock, next = -1;
|
|
static time_t start_time[ICON_MODE_SOCKS];
|
|
time_t oldest = 0;
|
|
int db = 0;
|
|
|
|
port = -1;
|
|
|
|
for (i = 0; i < ICON_MODE_SOCKS; i++) {
|
|
if (icon_mode_socks[i] < 0) {
|
|
next = i;
|
|
break;
|
|
}
|
|
if (oldest == 0 || start_time[i] < oldest) {
|
|
next = i;
|
|
oldest = start_time[i];
|
|
}
|
|
}
|
|
|
|
p = strtok(str, ":");
|
|
i = 0;
|
|
while (p) {
|
|
if (i == 0) {
|
|
host = strdup(p);
|
|
} else if (i == 1) {
|
|
port = atoi(p);
|
|
} else if (i == 2) {
|
|
cookie = strdup(p);
|
|
}
|
|
i++;
|
|
p = strtok(NULL, ":");
|
|
}
|
|
free(str);
|
|
|
|
if (db) fprintf(stderr, "%s/%d/%s next=%d\n", host, port, cookie, next);
|
|
|
|
if (host && port && cookie) {
|
|
if (*host == '\0') {
|
|
free(host);
|
|
host = strdup("localhost");
|
|
}
|
|
sock = rfbConnectToTcpAddr(host, port);
|
|
if (sock < 0) {
|
|
usleep(200 * 1000);
|
|
sock = rfbConnectToTcpAddr(host, port);
|
|
}
|
|
if (sock >= 0) {
|
|
char *lst = list_clients();
|
|
icon_mode_socks[next] = sock;
|
|
start_time[next] = time(NULL);
|
|
write(sock, "COOKIE:", strlen("COOKIE:"));
|
|
write(sock, cookie, strlen(cookie));
|
|
write(sock, "\n", strlen("\n"));
|
|
write(sock, "none\n", strlen("none\n"));
|
|
write(sock, "none\n", strlen("none\n"));
|
|
write(sock, lst, strlen(lst));
|
|
write(sock, "\n", strlen("\n"));
|
|
if (db) {
|
|
fprintf(stderr, "list: %s\n", lst);
|
|
}
|
|
free(lst);
|
|
rfbLog("client_info_sock to: %s:%d\n", host, port);
|
|
} else {
|
|
rfbLog("failed client_info_sock: %s:%d\n", host, port);
|
|
}
|
|
} else {
|
|
rfbLog("malformed client_info_sock: %s\n", host_port_cookie);
|
|
}
|
|
|
|
if (host) free(host);
|
|
if (cookie) free(cookie);
|
|
}
|
|
|
|
void send_client_info(char *str) {
|
|
int i;
|
|
static char *pstr = NULL;
|
|
static int len = 128;
|
|
|
|
if (!str || strlen(str) == 0) {
|
|
return;
|
|
}
|
|
|
|
if (!pstr) {
|
|
pstr = (char *)malloc(len);
|
|
}
|
|
if (strlen(str) + 2 > (size_t) len) {
|
|
free(pstr);
|
|
len *= 2;
|
|
pstr = (char *)malloc(len);
|
|
}
|
|
strcpy(pstr, str);
|
|
strcat(pstr, "\n");
|
|
|
|
if (icon_mode_fh) {
|
|
if (0) fprintf(icon_mode_fh, "\n");
|
|
fprintf(icon_mode_fh, "%s", pstr);
|
|
fflush(icon_mode_fh);
|
|
}
|
|
|
|
for (i=0; i<ICON_MODE_SOCKS; i++) {
|
|
int len, n, sock = icon_mode_socks[i];
|
|
char *buf = pstr;
|
|
|
|
if (sock < 0) {
|
|
continue;
|
|
}
|
|
|
|
len = strlen(pstr);
|
|
while (len > 0) {
|
|
if (0) write(sock, "\n", 1);
|
|
n = write(sock, buf, len);
|
|
if (n > 0) {
|
|
buf += n;
|
|
len -= n;
|
|
continue;
|
|
}
|
|
|
|
if (n < 0 && errno == EINTR) {
|
|
continue;
|
|
}
|
|
close(sock);
|
|
icon_mode_socks[i] = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void adjust_grabs(int grab, int quiet) {
|
|
RAWFB_RET_VOID
|
|
#if NO_X11
|
|
if (!grab || !quiet) {}
|
|
return;
|
|
#else
|
|
/* n.b. caller decides to X_LOCK or not. */
|
|
if (grab) {
|
|
if (grab_kbd) {
|
|
if (! quiet) {
|
|
rfbLog("grabbing keyboard with XGrabKeyboard\n");
|
|
}
|
|
XGrabKeyboard(dpy, window, False, GrabModeAsync,
|
|
GrabModeAsync, CurrentTime);
|
|
}
|
|
if (grab_ptr) {
|
|
if (! quiet) {
|
|
rfbLog("grabbing pointer with XGrabPointer\n");
|
|
}
|
|
XGrabPointer(dpy, window, False, 0, GrabModeAsync,
|
|
GrabModeAsync, None, None, CurrentTime);
|
|
}
|
|
} else {
|
|
if (grab_kbd) {
|
|
if (! quiet) {
|
|
rfbLog("ungrabbing keyboard with XUngrabKeyboard\n");
|
|
}
|
|
XUngrabKeyboard(dpy, CurrentTime);
|
|
}
|
|
if (grab_ptr) {
|
|
if (! quiet) {
|
|
rfbLog("ungrabbing pointer with XUngrabPointer\n");
|
|
}
|
|
XUngrabPointer(dpy, CurrentTime);
|
|
}
|
|
}
|
|
#endif /* NO_X11 */
|
|
}
|
|
|
|
void check_new_clients(void) {
|
|
static int last_count = 0;
|
|
rfbClientIteratorPtr iter;
|
|
rfbClientPtr cl;
|
|
int i, send_info = 0;
|
|
int run_after_accept = 0;
|
|
|
|
if (unixpw_in_progress) {
|
|
if (unixpw_client && unixpw_client->viewOnly) {
|
|
unixpw_login_viewonly = 1;
|
|
unixpw_client->viewOnly = FALSE;
|
|
}
|
|
if (time(NULL) > unixpw_last_try_time + 25) {
|
|
rfbLog("unixpw_deny: timed out waiting for reply.\n");
|
|
unixpw_deny();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (grab_always) {
|
|
;
|
|
} else if (grab_kbd || grab_ptr) {
|
|
static double last_force = 0.0;
|
|
if (client_count != last_count || dnow() > last_force + 0.25) {
|
|
int q = (client_count == last_count);
|
|
last_force = dnow();
|
|
X_LOCK;
|
|
if (client_count) {
|
|
adjust_grabs(1, q);
|
|
} else {
|
|
adjust_grabs(0, q);
|
|
}
|
|
X_UNLOCK;
|
|
}
|
|
}
|
|
|
|
if (client_count == last_count) {
|
|
return;
|
|
}
|
|
|
|
if (! all_clients_initialized()) {
|
|
return;
|
|
}
|
|
|
|
if (client_count > last_count) {
|
|
if (afteraccept_cmd != NULL && afteraccept_cmd[0] != '\0') {
|
|
run_after_accept = 1;
|
|
}
|
|
}
|
|
|
|
last_count = client_count;
|
|
|
|
if (! screen) {
|
|
return;
|
|
}
|
|
|
|
if (! client_count) {
|
|
send_client_info("clients:none");
|
|
return;
|
|
}
|
|
|
|
iter = rfbGetClientIterator(screen);
|
|
while( (cl = rfbClientIteratorNext(iter)) ) {
|
|
ClientData *cd = (ClientData *) cl->clientData;
|
|
char *s;
|
|
|
|
if (! cd) {
|
|
continue;
|
|
}
|
|
|
|
if (cd->login_viewonly < 0) {
|
|
/* this is a general trigger to initialize things */
|
|
if (cl->viewOnly) {
|
|
cd->login_viewonly = 1;
|
|
s = allowed_input_view_only;
|
|
if (s && cd->input[0] == '-') {
|
|
cl->viewOnly = FALSE;
|
|
cd->input[0] = '\0';
|
|
strncpy(cd->input, s, CILEN);
|
|
}
|
|
} else {
|
|
cd->login_viewonly = 0;
|
|
s = allowed_input_normal;
|
|
if (s && cd->input[0] == '-') {
|
|
cd->input[0] = '\0';
|
|
strncpy(cd->input, s, CILEN);
|
|
}
|
|
}
|
|
if (run_after_accept) {
|
|
run_user_command(afteraccept_cmd, cl,
|
|
"afteraccept", NULL, 0, NULL);
|
|
}
|
|
}
|
|
}
|
|
rfbReleaseClientIterator(iter);
|
|
|
|
if (icon_mode_fh) {
|
|
send_info++;
|
|
}
|
|
for (i = 0; i < ICON_MODE_SOCKS; i++) {
|
|
if (send_info || icon_mode_socks[i] >= 0) {
|
|
send_info++;
|
|
break;
|
|
}
|
|
}
|
|
if (send_info) {
|
|
char *str, *s = list_clients();
|
|
str = (char *) malloc(strlen("clients:") + strlen(s) + 1);
|
|
sprintf(str, "clients:%s", s);
|
|
send_client_info(str);
|
|
free(str);
|
|
free(s);
|
|
}
|
|
}
|
|
|