aboutsummaryrefslogtreecommitdiff
path: root/third_party/websockify/other/websockify.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/websockify/other/websockify.c')
-rw-r--r--third_party/websockify/other/websockify.c385
1 files changed, 385 insertions, 0 deletions
diff --git a/third_party/websockify/other/websockify.c b/third_party/websockify/other/websockify.c
new file mode 100644
index 00000000..40321715
--- /dev/null
+++ b/third_party/websockify/other/websockify.c
@@ -0,0 +1,385 @@
+/*
+ * 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" \
+ " --verbose|-v verbose messages and per frame traffic\n" \
+ " --daemon|-D become a daemon (background process)\n" \
+ " --cert CERT SSL certificate file\n" \
+ " --key KEY SSL key file (if separate from cert)\n" \
+ " --ssl-only disallow non-encrypted 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;
+
+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 opcode, left, ret;
+ unsigned int tout_start, tout_end, cout_start, cout_end;
+ unsigned int tin_start, tin_end;
+ ssize_t len, bytes;
+
+ tout_start = tout_end = cout_start = cout_end;
+ tin_start = tin_end = 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 (tout_end == tout_start) {
+ // 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 (cout_end == cout_start) {
+ // 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 = tout_end-tout_start;
+ bytes = send(target, ws_ctx->tout_buf + tout_start, len, 0);
+ if (pipe_error) { break; }
+ if (bytes < 0) {
+ handler_emsg("target connection error: %s\n",
+ strerror(errno));
+ break;
+ }
+ tout_start += bytes;
+ if (tout_start >= tout_end) {
+ tout_start = tout_end = 0;
+ traffic(">");
+ } else {
+ traffic(">.");
+ }
+ }
+
+ if (FD_ISSET(client, &wlist)) {
+ len = cout_end-cout_start;
+ bytes = ws_send(ws_ctx, ws_ctx->cout_buf + cout_start, len);
+ if (pipe_error) { break; }
+ if (len < 3) {
+ handler_emsg("len: %d, bytes: %d: %d\n",
+ (int) len, (int) bytes,
+ (int) *(ws_ctx->cout_buf + cout_start));
+ }
+ cout_start += bytes;
+ if (cout_start >= cout_end) {
+ cout_start = cout_end = 0;
+ traffic("<");
+ } else {
+ traffic("<.");
+ }
+ }
+
+ if (FD_ISSET(target, &rlist)) {
+ bytes = recv(target, ws_ctx->cin_buf, DBUFSIZE , 0);
+ if (pipe_error) { break; }
+ if (bytes <= 0) {
+ handler_emsg("target closed connection\n");
+ break;
+ }
+ cout_start = 0;
+ if (ws_ctx->hybi) {
+ cout_end = encode_hybi(ws_ctx->cin_buf, bytes,
+ ws_ctx->cout_buf, BUFSIZE, 1);
+ } else {
+ cout_end = encode_hixie(ws_ctx->cin_buf, bytes,
+ ws_ctx->cout_buf, BUFSIZE);
+ }
+ /*
+ printf("encoded: ");
+ for (i=0; i< cout_end; i++) {
+ printf("%u,", (unsigned char) *(ws_ctx->cout_buf+i));
+ }
+ printf("\n");
+ */
+ if (cout_end < 0) {
+ handler_emsg("encoding error\n");
+ break;
+ }
+ traffic("{");
+ }
+
+ if (FD_ISSET(client, &rlist)) {
+ bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1);
+ if (pipe_error) { break; }
+ if (bytes <= 0) {
+ handler_emsg("client closed connection\n");
+ break;
+ }
+ tin_end += bytes;
+ /*
+ printf("before decode: ");
+ for (i=0; i< bytes; i++) {
+ printf("%u,", (unsigned char) *(ws_ctx->tin_buf+i));
+ }
+ printf("\n");
+ */
+ if (ws_ctx->hybi) {
+ len = decode_hybi(ws_ctx->tin_buf + tin_start,
+ tin_end-tin_start,
+ ws_ctx->tout_buf, BUFSIZE-1,
+ &opcode, &left);
+ } else {
+ len = decode_hixie(ws_ctx->tin_buf + tin_start,
+ tin_end-tin_start,
+ ws_ctx->tout_buf, BUFSIZE-1,
+ &opcode, &left);
+ }
+
+ if (opcode == 8) {
+ handler_emsg("client sent orderly close frame\n");
+ break;
+ }
+
+ /*
+ printf("decoded: ");
+ for (i=0; i< len; i++) {
+ printf("%u,", (unsigned char) *(ws_ctx->tout_buf+i));
+ }
+ printf("\n");
+ */
+ if (len < 0) {
+ handler_emsg("decoding error\n");
+ break;
+ }
+ if (left) {
+ tin_start = tin_end - left;
+ //printf("partial frame from client");
+ } else {
+ tin_start = 0;
+ tin_end = 0;
+ }
+
+ traffic("}");
+ tout_start = 0;
+ tout_end = 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.verbose) && (! settings.daemon)) {
+ printf("%s", traffic_legend);
+ }
+
+ do_proxy(ws_ctx, tsock);
+
+ shutdown(tsock, SHUT_RDWR);
+ close(tsock);
+}
+
+int main(int argc, char *argv[])
+{
+ int fd, c, option_index = 0;
+ static int ssl_only = 0, daemon = 0, run_once = 0, verbose = 0;
+ char *found;
+ static struct option long_options[] = {
+ {"verbose", no_argument, &verbose, 'v'},
+ {"ssl-only", no_argument, &ssl_only, 1 },
+ {"daemon", no_argument, &daemon, 'D'},
+ /* ---- */
+ {"run-once", no_argument, 0, 'r'},
+ {"cert", required_argument, 0, 'c'},
+ {"key", required_argument, 0, 'k'},
+ {0, 0, 0, 0}
+ };
+
+ settings.cert = realpath("self.pem", NULL);
+ if (!settings.cert) {
+ /* Make sure it's always set to something */
+ settings.cert = "self.pem";
+ }
+ settings.key = "";
+
+ while (1) {
+ c = getopt_long (argc, argv, "vDrc:k:",
+ long_options, &option_index);
+
+ /* Detect the end */
+ if (c == -1) { break; }
+
+ switch (c) {
+ case 0:
+ break; // ignore
+ case 1:
+ break; // ignore
+ case 'v':
+ verbose = 1;
+ break;
+ case 'D':
+ daemon = 1;
+ break;
+ case 'r':
+ run_once = 1;
+ break;
+ case 'c':
+ settings.cert = realpath(optarg, NULL);
+ if (! settings.cert) {
+ usage("No cert file at %s\n", optarg);
+ }
+ break;
+ case 'k':
+ settings.key = realpath(optarg, NULL);
+ if (! settings.key) {
+ usage("No key file at %s\n", optarg);
+ }
+ break;
+ default:
+ usage("");
+ }
+ }
+ settings.verbose = verbose;
+ settings.ssl_only = ssl_only;
+ settings.daemon = daemon;
+ settings.run_once = run_once;
+
+ 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) {
+ if (access(settings.cert, R_OK) != 0) {
+ usage("SSL only and cert file '%s' not found\n", settings.cert);
+ }
+ } else if (access(settings.cert, R_OK) != 0) {
+ fprintf(stderr, "Warning: '%s' not found\n", settings.cert);
+ }
+
+ //printf(" verbose: %d\n", settings.verbose);
+ //printf(" ssl_only: %d\n", settings.ssl_only);
+ //printf(" daemon: %d\n", settings.daemon);
+ //printf(" run_once: %d\n", settings.run_once);
+ //printf(" cert: %s\n", settings.cert);
+ //printf(" key: %s\n", settings.key);
+
+ settings.handler = proxy_handler;
+ start_server();
+
+}