diff options
Diffstat (limited to 'x11vnc/connections.c')
-rw-r--r-- | x11vnc/connections.c | 564 |
1 files changed, 555 insertions, 9 deletions
diff --git a/x11vnc/connections.c b/x11vnc/connections.c index ac0723d..b7af0be 100644 --- a/x11vnc/connections.c +++ b/x11vnc/connections.c @@ -1610,6 +1610,544 @@ static void check_connect_file(char *file) { } } +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; +} + /* * Do a reverse connect for a single "host" or "host:port" */ @@ -1654,15 +2192,13 @@ static int do_reverse_connect(char *str) { *p = '\0'; } -#if 0 - if (use_openssl && !getenv("X11VNC_SSL_ALLOW_REVERSE")) { - rfbLog("reverse connections disabled in -ssl mode.\n"); - return 0; - } -#endif - if (use_openssl) { - int vncsock = rfbConnectToTcpAddr(host, rport); + 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; @@ -1702,7 +2238,17 @@ static int do_reverse_connect(char *str) { } } - cl = rfbReverseConnection(screen, host, rport); + if (connect_proxy != NULL) { + int sock = proxy_connect(host, rport); + if (sock >= 0) { + cl = rfbNewClient(screen, sock); + } else { + return 0; + } + } else { + cl = rfbReverseConnection(screen, host, rport); + } + free(host); if (cl == NULL) { |