mirror of
https://github.com/novnc/noVNC.git
synced 2026-05-27 15:39:41 +00:00
Add -m, --multiprocess option which forks a handler for each connection allowing multiple connections to the same target using the same proxy instance. Cleaned up the output of the handler process. Each process' output is prefixed with an ordinal value. Changed both the C and python versions of the proxy.
345 lines
9.8 KiB
C
345 lines
9.8 KiB
C
/*
|
|
* A WebSocket to TCP socket proxy with support for "wss://" encryption.
|
|
* Copyright 2010 Joel Martin
|
|
* Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3)
|
|
*
|
|
* You can make a cert/key with openssl using:
|
|
* openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem
|
|
* as taken from http://docs.python.org/dev/library/ssl.html#certificates
|
|
*/
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <getopt.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#include <sys/select.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include "websocket.h"
|
|
|
|
char traffic_legend[] = "\n\
|
|
Traffic Legend:\n\
|
|
} - Client receive\n\
|
|
}. - Client receive partial\n\
|
|
{ - Target receive\n\
|
|
\n\
|
|
> - Target send\n\
|
|
>. - Target send partial\n\
|
|
< - Client send\n\
|
|
<. - Client send partial\n\
|
|
";
|
|
|
|
char USAGE[] = "Usage: [options] " \
|
|
"[source_addr:]source_port target_addr:target_port\n\n" \
|
|
" --cert CERT load CERT as SSL certificate\n" \
|
|
" --foreground|-f run in the foreground\n" \
|
|
" --ssl-only disallow non-SSL connections";
|
|
|
|
#define usage(fmt, args...) \
|
|
fprintf(stderr, "%s\n\n", USAGE); \
|
|
fprintf(stderr, fmt , ## args); \
|
|
exit(1);
|
|
|
|
char target_host[256];
|
|
int target_port;
|
|
|
|
extern pipe_error;
|
|
extern settings_t settings;
|
|
extern char *tbuf, *cbuf, *tbuf_tmp, *cbuf_tmp;
|
|
extern unsigned int bufsize, dbufsize;
|
|
|
|
void do_proxy(ws_ctx_t *ws_ctx, int target) {
|
|
fd_set rlist, wlist, elist;
|
|
struct timeval tv;
|
|
int i, maxfd, client = ws_ctx->sockfd;
|
|
unsigned int tstart, tend, cstart, cend, ret;
|
|
ssize_t len, bytes;
|
|
|
|
tstart = tend = cstart = cend = 0;
|
|
maxfd = client > target ? client+1 : target+1;
|
|
|
|
while (1) {
|
|
tv.tv_sec = 1;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&rlist);
|
|
FD_ZERO(&wlist);
|
|
FD_ZERO(&elist);
|
|
|
|
FD_SET(client, &elist);
|
|
FD_SET(target, &elist);
|
|
|
|
if (tend == tstart) {
|
|
// Nothing queued for target, so read from client
|
|
FD_SET(client, &rlist);
|
|
} else {
|
|
// Data queued for target, so write to it
|
|
FD_SET(target, &wlist);
|
|
}
|
|
if (cend == cstart) {
|
|
// Nothing queued for client, so read from target
|
|
FD_SET(target, &rlist);
|
|
} else {
|
|
// Data queued for client, so write to it
|
|
FD_SET(client, &wlist);
|
|
}
|
|
|
|
ret = select(maxfd, &rlist, &wlist, &elist, &tv);
|
|
if (pipe_error) { break; }
|
|
|
|
if (FD_ISSET(target, &elist)) {
|
|
handler_emsg("target exception\n");
|
|
break;
|
|
}
|
|
if (FD_ISSET(client, &elist)) {
|
|
handler_emsg("client exception\n");
|
|
break;
|
|
}
|
|
|
|
if (ret == -1) {
|
|
handler_emsg("select(): %s\n", strerror(errno));
|
|
break;
|
|
} else if (ret == 0) {
|
|
//handler_emsg("select timeout\n");
|
|
continue;
|
|
}
|
|
|
|
if (FD_ISSET(target, &wlist)) {
|
|
len = tend-tstart;
|
|
bytes = send(target, tbuf + tstart, len, 0);
|
|
if (pipe_error) { break; }
|
|
if (bytes < 0) {
|
|
handler_emsg("target connection error: %s\n",
|
|
strerror(errno));
|
|
break;
|
|
}
|
|
tstart += bytes;
|
|
if (tstart >= tend) {
|
|
tstart = tend = 0;
|
|
traffic(">");
|
|
} else {
|
|
traffic(">.");
|
|
}
|
|
}
|
|
|
|
if (FD_ISSET(client, &wlist)) {
|
|
len = cend-cstart;
|
|
bytes = ws_send(ws_ctx, cbuf + cstart, len);
|
|
if (pipe_error) { break; }
|
|
if (len < 3) {
|
|
handler_emsg("len: %d, bytes: %d: %d\n", len, bytes, *(cbuf + cstart));
|
|
}
|
|
cstart += bytes;
|
|
if (cstart >= cend) {
|
|
cstart = cend = 0;
|
|
traffic("<");
|
|
} else {
|
|
traffic("<.");
|
|
}
|
|
}
|
|
|
|
if (FD_ISSET(target, &rlist)) {
|
|
bytes = recv(target, cbuf_tmp, dbufsize , 0);
|
|
if (pipe_error) { break; }
|
|
if (bytes <= 0) {
|
|
handler_emsg("target closed connection\n");
|
|
break;
|
|
}
|
|
cstart = 0;
|
|
cend = encode(cbuf_tmp, bytes, cbuf, bufsize);
|
|
/*
|
|
printf("encoded: ");
|
|
for (i=0; i< cend; i++) {
|
|
printf("%u,", (unsigned char) *(cbuf+i));
|
|
}
|
|
printf("\n");
|
|
*/
|
|
if (cend < 0) {
|
|
handler_emsg("encoding error\n");
|
|
break;
|
|
}
|
|
traffic("{");
|
|
}
|
|
|
|
if (FD_ISSET(client, &rlist)) {
|
|
bytes = ws_recv(ws_ctx, tbuf_tmp, bufsize-1);
|
|
if (pipe_error) { break; }
|
|
if (bytes <= 0) {
|
|
handler_emsg("client closed connection\n");
|
|
break;
|
|
} else if ((bytes == 2) &&
|
|
(tbuf_tmp[0] == '\xff') &&
|
|
(tbuf_tmp[1] == '\x00')) {
|
|
handler_emsg("client sent orderly close frame\n");
|
|
break;
|
|
}
|
|
/*
|
|
printf("before decode: ");
|
|
for (i=0; i< bytes; i++) {
|
|
printf("%u,", (unsigned char) *(tbuf_tmp+i));
|
|
}
|
|
printf("\n");
|
|
*/
|
|
len = decode(tbuf_tmp, bytes, tbuf, bufsize-1);
|
|
/*
|
|
printf("decoded: ");
|
|
for (i=0; i< len; i++) {
|
|
printf("%u,", (unsigned char) *(tbuf+i));
|
|
}
|
|
printf("\n");
|
|
*/
|
|
if (len < 0) {
|
|
handler_emsg("decoding error\n");
|
|
break;
|
|
}
|
|
traffic("}");
|
|
tstart = 0;
|
|
tend = len;
|
|
}
|
|
}
|
|
}
|
|
|
|
void proxy_handler(ws_ctx_t *ws_ctx) {
|
|
int tsock = 0;
|
|
struct sockaddr_in taddr;
|
|
|
|
handler_msg("connecting to: %s:%d\n", target_host, target_port);
|
|
|
|
tsock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (tsock < 0) {
|
|
handler_emsg("Could not create target socket: %s\n",
|
|
strerror(errno));
|
|
return;
|
|
}
|
|
bzero((char *) &taddr, sizeof(taddr));
|
|
taddr.sin_family = AF_INET;
|
|
taddr.sin_port = htons(target_port);
|
|
|
|
/* Resolve target address */
|
|
if (resolve_host(&taddr.sin_addr, target_host) < -1) {
|
|
handler_emsg("Could not resolve target address: %s\n",
|
|
strerror(errno));
|
|
}
|
|
|
|
if (connect(tsock, (struct sockaddr *) &taddr, sizeof(taddr)) < 0) {
|
|
handler_emsg("Could not connect to target: %s\n",
|
|
strerror(errno));
|
|
close(tsock);
|
|
return;
|
|
}
|
|
|
|
if ((! settings.daemon) && (! settings.multiprocess)) {
|
|
printf("%s", traffic_legend);
|
|
}
|
|
|
|
do_proxy(ws_ctx, tsock);
|
|
|
|
close(tsock);
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int fd, c, option_index = 0;
|
|
static int ssl_only = 0, foreground = 0, multi = 0;
|
|
char *found;
|
|
static struct option long_options[] = {
|
|
{"ssl-only", no_argument, &ssl_only, 1 },
|
|
{"foreground", no_argument, &foreground, 'f'},
|
|
{"multiprocess", no_argument, &multi, 'm'},
|
|
/* ---- */
|
|
{"cert", required_argument, 0, 'c'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
settings.cert = realpath("self.pem", NULL);
|
|
|
|
while (1) {
|
|
c = getopt_long (argc, argv, "fmc:",
|
|
long_options, &option_index);
|
|
|
|
/* Detect the end */
|
|
if (c == -1) { break; }
|
|
|
|
switch (c) {
|
|
case 0:
|
|
break; // ignore
|
|
case 1:
|
|
break; // ignore
|
|
case 'f':
|
|
foreground = 1;
|
|
break;
|
|
case 'm':
|
|
multi = 1;
|
|
break;
|
|
case 'r':
|
|
if ((fd = open(optarg, O_CREAT,
|
|
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < -1) {
|
|
usage("Could not access %s\n", optarg);
|
|
}
|
|
close(fd);
|
|
break;
|
|
case 'c':
|
|
settings.cert = realpath(optarg, NULL);
|
|
if (! settings.cert) {
|
|
usage("No cert file at %s\n", optarg);
|
|
}
|
|
break;
|
|
default:
|
|
usage("");
|
|
}
|
|
}
|
|
settings.ssl_only = ssl_only;
|
|
settings.daemon = foreground ? 0: 1;
|
|
settings.multiprocess = multi;
|
|
|
|
if ((argc-optind) != 2) {
|
|
usage("Invalid number of arguments\n");
|
|
}
|
|
|
|
found = strstr(argv[optind], ":");
|
|
if (found) {
|
|
memcpy(settings.listen_host, argv[optind], found-argv[optind]);
|
|
settings.listen_port = strtol(found+1, NULL, 10);
|
|
} else {
|
|
settings.listen_host[0] = '\0';
|
|
settings.listen_port = strtol(argv[optind], NULL, 10);
|
|
}
|
|
optind++;
|
|
if (settings.listen_port == 0) {
|
|
usage("Could not parse listen_port\n");
|
|
}
|
|
|
|
found = strstr(argv[optind], ":");
|
|
if (found) {
|
|
memcpy(target_host, argv[optind], found-argv[optind]);
|
|
target_port = strtol(found+1, NULL, 10);
|
|
} else {
|
|
usage("Target argument must be host:port\n");
|
|
}
|
|
if (target_port == 0) {
|
|
usage("Could not parse target port\n");
|
|
}
|
|
|
|
if (ssl_only) {
|
|
printf("cert: %s\n", settings.cert);
|
|
if (!settings.cert || !access(settings.cert)) {
|
|
usage("SSL only and cert file not found\n");
|
|
}
|
|
}
|
|
|
|
//printf(" ssl_only: %d\n", settings.ssl_only);
|
|
//printf(" daemon: %d\n", settings.daemon);
|
|
//printf(" multiproces: %d\n", settings.multiprocess);
|
|
//printf(" cert: %s\n", settings.cert);
|
|
|
|
settings.handler = proxy_handler;
|
|
start_server();
|
|
|
|
free(tbuf);
|
|
free(cbuf);
|
|
free(tbuf_tmp);
|
|
free(cbuf_tmp);
|
|
}
|